如何从 ArrayList 中删除重复的元素?

Posted

技术标签:

【中文标题】如何从 ArrayList 中删除重复的元素?【英文标题】:How do I remove repeated elements from ArrayList? 【发布时间】:2022-01-17 01:24:29 【问题描述】:

我有一个ArrayList<String>,我想从中删除重复的字符串。我该怎么做?

【问题讨论】:

【参考方案1】:

如果您不希望在 Collection 中出现重复,您应该考虑为什么要使用允许重复的 Collection。删除重复元素的最简单方法是将内容添加到Set(不允许重复),然后将Set 添加回ArrayList

Set<String> set = new HashSet<>(yourList);
yourList.clear();
yourList.addAll(set);

当然,这会破坏ArrayList 中元素的顺序。

【讨论】:

如果您希望保留订单,另请参阅 LinkedHashSet。 @Chetan 在 O(n) 中从 ArrayList 中找到所有重复项,正确定义列表中对象的 equals 方法很重要(数字没问题):public Set&lt;Object&gt; findDuplicates(List&lt;Object&gt; list) Set&lt;Object&gt; items = new HashSet&lt;Object&gt;(); Set&lt;Object&gt; duplicates = new HashSet&lt;Object&gt;(); for (Object item : list) if (items.contains(item)) duplicates.add(item); else items.add(item); return duplicates; 一个好的做法是使用接口类型 ListSet 定义变量(而不是在您的示例中实现类型 ArrayListHashSet)。 您可以使用new HashSet(al) 来清理它,而不是将其初始化为空并调用addAll 我可以添加规则来设置对我来说重复的内容吗?例如:当我的Object 有多个值如果其中两个重复时,我认为它们是重复的(其他值可以不同)并使用Set?【参考方案2】:

虽然将 ArrayList 转换为 HashSet 可以有效地删除重复项,但如果您需要保留插入顺序,我建议您使用此变体

// list is some List of Strings
Set<String> s = new LinkedHashSet<>(list);

然后,如果您需要取回List 引用,您可以再次使用转换构造函数。

【讨论】:

LinkedHashSet 是否保证从列表中保留几个重复项中的哪一个?例如,如果位置 1、3 和 5 在原始列表中是重复的,我们是否可以假设此过程将删除 3 和 5?或者也许删除1和3?谢谢。 @Matt:是的,它确实保证了这一点。 docs 说:“这个链表定义了迭代顺序,即元素插入集合的顺序(插入顺序)。请注意,如果将元素重新插入集合,则插入顺序不受影响。” 非常有趣。我这里有不同的情况。我不是要对字符串进行排序,而是要对另一个名为 AwardYearSource 的对象进行排序。这个类有一个叫做 year 的 int 属性。所以我想根据年份删除重复项。即如果不止一次提到 2010 年,我想删除该 AwardYearSource 对象。我该怎么做? @WowBow 例如,您可以定义包含 AwardYearSource 的 Wrapper 对象。并根据 AwardYearSources 年份字段定义此 Wrapper 对象 equals 方法。然后您可以将 Set 与这些 Wrapper 对象一起使用。 @WowBow 或实现 Comparable/Comparator【参考方案3】:

在 Java 8 中:

List<String> deduped = list.stream().distinct().collect(Collectors.toList());

请注意,应遵守列表成员的hashCode-equals 合同,以便过滤正常工作。

【讨论】:

我如何为不区分大小写的 distinct 执行此操作? @StackFlowed 如果您不需要保留列表的顺序,您可以addAllnew TreeSet&lt;String&gt;(String.CASE_INSENSITIVE_ORDER)。添加的第一个元素将保留在集合中,因此如果您的列表包含“Dog”和“dog”(按此顺序),TreeSet 将包含“Dog”。如果必须保留顺序,则在答案中的行之前放置list.replaceAll(String::toUpperCase); 我收到此错误:不兼容的类型:List 无法转换为 List 这通常是一个简单的解决方案,但是如何从 int[] 的 Arraylist 中删除重复项?【参考方案4】:

假设我们有一个String 的列表,比如:

List<String> strList = new ArrayList<>(5);
// insert up to five items to list.        

然后我们可以通过多种方式删除重复元素。

Java 8 之前

List<String> deDupStringList = new ArrayList<>(new HashSet<>(strList));

注意:如果我们想保持插入顺序,那么我们需要使用LinkedHashSet 代替HashSet

使用番石榴

List<String> deDupStringList2 = Lists.newArrayList(Sets.newHashSet(strList));

使用 Java 8

List<String> deDupStringList3 = strList.stream().distinct().collect(Collectors.toList());

注意:如果我们想在特定列表实现中收集结果,例如LinkedList 那么我们可以将上面的例子修改为:

List<String> deDupStringList3 = strList.stream().distinct()
                 .collect(Collectors.toCollection(LinkedList::new));

我们也可以在上面的代码中使用parallelStream,但它可能不会带来预期的性能优势。查看此question 了解更多信息。

【讨论】:

是的,当我输入我以前的 cmets 时,我的印象是 parallel streams 将始终提供更好的性能。但这是一个神话。后来我了解到,在某些情况下应该使用并行流。在这种情况下,并行流不会提供任何更好的性能。是的,并行流在某些情况下可能不会给出预期的结果。 List&lt;String&gt; deDupStringList3 = stringList.stream().map(String::toLowerCase).distinct().collect(Collectors.toList()); 在这种情况下应该是合适的解决方案【参考方案5】:

如果您不想重复,请使用Set 而不是List。要将List 转换为Set,您可以使用以下代码:

// list is some List of Strings
Set<String> s = new HashSet<String>(list);

如果确实需要,您可以使用相同的结构将 Set 转换回 List

【讨论】:

类似地在线程的底部,我给出了一个答案,我使用 Set for Custom Object。如果有人有像“联系人”或“学生”这样的自定义对象,可以使用对我来说很好的答案。 当您必须专门访问一个元素时,问题就出现了。例如,当在 android 中将对象绑定到列表项视图时,您会得到它的索引。所以这里不能使用Set 当列表是对象列表时,我该如何解决这个问题【参考方案6】:

你也可以这样做,并保持秩序:

// delete duplicates (if any) from 'myArrayList'
myArrayList = new ArrayList<String>(new LinkedHashSet<String>(myArrayList));

【讨论】:

我认为这是删除 ArrayList 中重复项的最佳方法。绝对推荐。谢谢@Nenad 的回答。【参考方案7】:

Java 8 流提供了一种从列表中删除重复元素的非常简单的方法。使用不同的方法。 如果我们有一个城市列表,并且我们想从该列表中删除重复项,则可以在一行中完成 -

 List<String> cityList = new ArrayList<>();
 cityList.add("Delhi");
 cityList.add("Mumbai");
 cityList.add("Bangalore");
 cityList.add("Chennai");
 cityList.add("Kolkata");
 cityList.add("Mumbai");

 cityList = cityList.stream().distinct().collect(Collectors.toList());

How to remove duplicate elements from an arraylist

【讨论】:

【参考方案8】:

这是一种不会影响您的列表排序的方法:

ArrayList l1 = new ArrayList();
ArrayList l2 = new ArrayList();

Iterator iterator = l1.iterator();

while (iterator.hasNext()) 
    YourClass o = (YourClass) iterator.next();
    if(!l2.contains(o)) l2.add(o);

l1是原始列表,l2是没有重复项的列表 (确保 YourClass 有 equals 方法,根据你想代表的平等)

【讨论】:

这个答案缺少两点:1)它不使用泛型,但是原始类型(应该使用ArrayList&lt;T&gt;而不是ArrayList)2)可以通过使用a来避免显式迭代器创建for (T current : l1) ... 。即使您想明确使用Iteratoriterador 也会拼写错误。 与以线性时间运行的链接哈希集实现相比,此实现以二次时间运行。 (即,在具有 10 个元素的列表上,这需要 10 倍的时间,在具有 10,000 个元素的列表上需要 10,000 倍的时间。ArrayList.contains 的 JDK 6 实现,JDK8 impl 是相同的。)【参考方案9】:

这样可以解决问题:

private List<SomeClass> clearListFromDuplicateFirstName(List<SomeClass> list1) 

     Map<String, SomeClass> cleanMap = new LinkedHashMap<String, SomeClass>();
     for (int i = 0; i < list1.size(); i++) 
         cleanMap.put(list1.get(i).getFirstName(), list1.get(i));
     
     List<SomeClass> list = new ArrayList<SomeClass>(cleanMap.values());
     return list;

【讨论】:

我更喜欢这个解决方案。【参考方案10】:

可以在不使用HashSet另一个arraylist 的情况下从arraylist 中删除重复项。

试试这个代码..

    ArrayList<String> lst = new ArrayList<String>();
    lst.add("ABC");
    lst.add("ABC");
    lst.add("ABCD");
    lst.add("ABCD");
    lst.add("ABCE");

    System.out.println("Duplicates List "+lst);

    Object[] st = lst.toArray();
      for (Object s : st) 
        if (lst.indexOf(s) != lst.lastIndexOf(s)) 
            lst.remove(lst.lastIndexOf(s));
         
      

    System.out.println("Distinct List "+lst);

输出是

Duplicates List [ABC, ABC, ABCD, ABCD, ABCE]
Distinct List [ABC, ABCD, ABCE]

【讨论】:

速度很慢,您可能会遇到 ConcurrentModificationException。 @maaartinus 你试过那个代码吗?它不会产生任何异常。而且它非常快。我在发布之前尝试了代码。 你是对的,当你迭代数组而不是列表时它不会。但是,它像地狱一样缓慢。尝试几百万个元素。将其与ImmutableSet.copyOf(lst).toList() 进行比较。 回答了我在面试中被问到的问题。如何在不使用 Sets 的情况下从 ArrayList 中删除重复值。谢谢 在内部,indexOf 使用 for 循环迭代 lst【参考方案11】:

还有来自Guava 的ImmutableSet 作为选项(here 是文档):

ImmutableSet.copyOf(list);

【讨论】:

请注意,有一个ImmutableSet.asList() 方法,如果您需要它作为List 返回一个ImmutableList【参考方案12】:

可能有点矫枉过正,但我​​喜欢这种孤立的问题。 :)

此代码使用临时 Set(用于唯一性检查),但直接删除原始列表中的元素。由于 ArrayList 中的元素删除会导致大量的数组复制,因此避免使用 remove(int) 方法。

public static <T> void removeDuplicates(ArrayList<T> list) 
    int size = list.size();
    int out = 0;
    
        final Set<T> encountered = new HashSet<T>();
        for (int in = 0; in < size; in++) 
            final T t = list.get(in);
            final boolean first = encountered.add(t);
            if (first) 
                list.set(out++, t);
            
        
    
    while (out < size) 
        list.remove(--size);
    

虽然我们在这里,但这里有一个 LinkedList 的版本(更好!):

public static <T> void removeDuplicates(LinkedList<T> list) 
    final Set<T> encountered = new HashSet<T>();
    for (Iterator<T> iter = list.iterator(); iter.hasNext(); ) 
        final T t = iter.next();
        final boolean first = encountered.add(t);
        if (!first) 
            iter.remove();
        
    

使用marker界面呈现List的统一解决方案:

public static <T> void removeDuplicates(List<T> list) 
    if (list instanceof RandomAccess) 
        // use first version here
     else 
        // use other version here
    

编辑:我猜泛型的东西在这里并没有真正增加任何价值。哦,好吧。 :)

【讨论】:

为什么在参数中使用ArrayList?为什么不只是列出?那不行吗? A List 绝对工作作为列出的第一个方法的参数。然而,该方法已优化以用于随机访问列表(例如 ArrayList),因此如果改为传递 LinkedList,则性能会很差。例如,在 LinkedList 中设置第 n 个元素需要 O(n) 时间,而在随机访问列表(例如 ArrayList)中设置第 n:th 个元素需要 O(1) 时间。不过,这也可能是矫枉过正……如果您需要这种专门的代码,那么它可能会处于孤立状态。【参考方案13】:
public static void main(String[] args)
    ArrayList<Object> al = new ArrayList<Object>();
    al.add("abc");
    al.add('a');
    al.add('b');
    al.add('a');
    al.add("abc");
    al.add(10.3);
    al.add('c');
    al.add(10);
    al.add("abc");
    al.add(10);
    System.out.println("Before Duplicate Remove:"+al);
    for(int i=0;i<al.size();i++)
        for(int j=i+1;j<al.size();j++)
            if(al.get(i).equals(al.get(j)))
                al.remove(j);
                j--;
            
        
    
    System.out.println("After Removing duplicate:"+al);

【讨论】:

此实现不返回列表中的任何元素,因为最后一个 j-- 这个实现工作非常好。这背后没有问题,对于这个任务,我只使用一个数组列表。所以这个答案是完全好的。在给出负面反馈之前,你还应该添加测试用例,这样每个人都能理解结果。谢谢 Manash【参考方案14】:

如果你愿意使用第三方库,可以使用Eclipse Collections(原GS Collections)中的distinct()方法。

ListIterable<Integer> integers = FastList.newListWith(1, 3, 1, 2, 2, 1);
Assert.assertEquals(
    FastList.newListWith(1, 3, 2),
    integers.distinct());

使用distinct()而不是转换为Set然后返回List的优点是distinct()保留了原始List的顺序,保留了每个元素的第一次出现。它是通过使用 Set 和 List 来实现的。

MutableSet<T> seenSoFar = UnifiedSet.newSet();
int size = list.size();
for (int i = 0; i < size; i++)

    T item = list.get(i);
    if (seenSoFar.add(item))
    
        targetCollection.add(item);
    

return targetCollection;

如果您无法将原始 List 转换为 Eclipse Collections 类型,则可以使用 ListAdapter 获取相同的 API。

MutableList<Integer> distinct = ListAdapter.adapt(integers).distinct();

注意:我是 Eclipse Collections 的提交者。

【讨论】:

【参考方案15】:

如果您使用模型类型 List/ArrayList 。希望对你有帮助。

这是我的代码,没有使用任何其他数据结构,例如 set 或 hashmap

for (int i = 0; i < Models.size(); i++)
for (int j = i + 1; j < Models.size(); j++)        
 if (Models.get(i).getName().equals(Models.get(j).getName()))     
 Models.remove(j);
   j--;
  
 

【讨论】:

【参考方案16】:

如果您想保留您的订单,那么最好使用 LinkedHashSet。 因为如果你想通过迭代将这个 List 传递给一个插入查询,顺序将被保留。

试试这个

LinkedHashSet link=new LinkedHashSet();
List listOfValues=new ArrayList();
listOfValues.add(link);

当您想要返回 List 而不是 Set 时,这种转换将非常有用。

【讨论】:

【参考方案17】:

这三行代码可以从 ArrayList 或任何集合中删除重复的元素。

List<Entity> entities = repository.findByUserId(userId);

Set<Entity> s = new LinkedHashSet<Entity>(entities);
entities.clear();
entities.addAll(s);

【讨论】:

【参考方案18】:

当您填充 ArrayList 时,请为每个元素使用一个条件。例如:

    ArrayList< Integer > al = new ArrayList< Integer >(); 

    // fill 1 
    for ( int i = 0; i <= 5; i++ ) 
        if ( !al.contains( i ) ) 
            al.add( i ); 

    // fill 2 
    for (int i = 0; i <= 10; i++ ) 
        if ( !al.contains( i ) ) 
            al.add( i ); 

    for( Integer i: al )
    
        System.out.print( i + " ");     
    

我们会得到一个数组 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

【讨论】:

【参考方案19】:

代码:

List<String> duplicatList = new ArrayList<String>();
duplicatList = Arrays.asList("AA","BB","CC","DD","DD","EE","AA","FF");
//above AA and DD are duplicate
Set<String> uniqueList = new HashSet<String>(duplicatList);
duplicatList = new ArrayList<String>(uniqueList); //let GC will doing free memory
System.out.println("Removed Duplicate : "+duplicatList);

注意:肯定会有内存开销。

【讨论】:

【参考方案20】:
ArrayList<String> city=new ArrayList<String>();
city.add("rajkot");
city.add("gondal");
city.add("rajkot");
city.add("gova");
city.add("baroda");
city.add("morbi");
city.add("gova");

HashSet<String> hashSet = new HashSet<String>();
hashSet.addAll(city);
city.clear();
city.addAll(hashSet);
Toast.makeText(getActivity(),"" + city.toString(),Toast.LENGTH_SHORT).show();

【讨论】:

【参考方案21】:

你可以在下面使用嵌套循环:

ArrayList<Class1> l1 = new ArrayList<Class1>();
ArrayList<Class1> l2 = new ArrayList<Class1>();

        Iterator iterator1 = l1.iterator();
        boolean repeated = false;

        while (iterator1.hasNext())
        
            Class1 c1 = (Class1) iterator1.next();
            for (Class1 _c: l2) 
                if(_c.getId() == c1.getId())
                    repeated = true;
            
            if(!repeated)
                l2.add(c1);
        

【讨论】:

完美 - 只是缺少“repeated = false;”在“if(!repeated) l2.add(c1);”之后的内部循环中否则返回一个短列表【参考方案22】:
for(int a=0;a<myArray.size();a++)
        for(int b=a+1;b<myArray.size();b++)
            if(myArray.get(a).equalsIgnoreCase(myArray.get(b)))
                myArray.remove(b); 
                dups++;
                b--;
            
        

【讨论】:

【参考方案23】:

LinkedHashSet 可以解决问题。

String[] arr2 = "5","1","2","3","3","4","1","2";
Set<String> set = new LinkedHashSet<String>(Arrays.asList(arr2));
for(String s1 : set)
    System.out.println(s1);

System.out.println( "------------------------" );
String[] arr3 = set.toArray(new String[0]);
for(int i = 0; i < arr3.length; i++)
     System.out.println(arr3[i].toString());

//输出:5,1,2,3,4

【讨论】:

【参考方案24】:
        List<String> result = new ArrayList<String>();
        Set<String> set = new LinkedHashSet<String>();
        String s = "ravi is a good!boy. But ravi is very nasty fellow.";
        StringTokenizer st = new StringTokenizer(s, " ,. ,!");
        while (st.hasMoreTokens()) 
            result.add(st.nextToken());
        
         System.out.println(result);
         set.addAll(result);
        result.clear();
        result.addAll(set);
        System.out.println(result);

output:
[ravi, is, a, good, boy, But, ravi, is, very, nasty, fellow]
[ravi, is, a, good, boy, But, very, nasty, fellow]

【讨论】:

【参考方案25】:

这用于您的自定义对象列表

   public List<Contact> removeDuplicates(List<Contact> list) 
    // Set set1 = new LinkedHashSet(list);
    Set set = new TreeSet(new Comparator() 

        @Override
        public int compare(Object o1, Object o2) 
            if (((Contact) o1).getId().equalsIgnoreCase(((Contact) o2).getId()) /*&&
                    ((Contact)o1).getName().equalsIgnoreCase(((Contact)o2).getName())*/) 
                return 0;
            
            return 1;
        
    );
    set.addAll(list);

    final List newList = new ArrayList(set);
    return newList;

【讨论】:

【参考方案26】:

如前所述,您应该使用实现 Set 接口的类而不是 List 来确保元素的唯一性。如果必须保持元素的顺序,则可以使用 SortedSet 接口; TreeSet 类实现了该接口。

【讨论】:

【参考方案27】:
import java.util.*;
class RemoveDupFrmString

    public static void main(String[] args)
    

        String s="appsc";

        Set<Character> unique = new LinkedHashSet<Character> ();

        for(char c : s.toCharArray()) 

            System.out.println(unique.add(c));
        
        for(char dis:unique)
            System.out.println(dis);
        


    

【讨论】:

【参考方案28】:
public Set<Object> findDuplicates(List<Object> list) 
        Set<Object> items = new HashSet<Object>();
        Set<Object> duplicates = new HashSet<Object>();
        for (Object item : list) 
            if (items.contains(item)) 
                duplicates.add(item);
                 else  
                    items.add(item);
                     
             
        return duplicates;
        

【讨论】:

【参考方案29】:
    ArrayList<String> list = new ArrayList<String>();
    HashSet<String> unique = new LinkedHashSet<String>();
    HashSet<String> dup = new LinkedHashSet<String>();
    boolean b = false;
    list.add("Hello");
    list.add("Hello");
    list.add("how");
    list.add("are");
    list.add("u");
    list.add("u");

    for(Iterator iterator= list.iterator();iterator.hasNext();)
    
        String value = (String)iterator.next();
        System.out.println(value);

        if(b==unique.add(value))
            dup.add(value);
        else
            unique.add(value);


    
    System.out.println(unique);
    System.out.println(dup);

【讨论】:

【参考方案30】:

如果你想从 ArrayList 中删除重复项意味着找到下面的逻辑,

public static Object[] removeDuplicate(Object[] inputArray)

    long startTime = System.nanoTime();
    int totalSize = inputArray.length;
    Object[] resultArray = new Object[totalSize];
    int newSize = 0;
    for(int i=0; i<totalSize; i++)
    
        Object value = inputArray[i];
        if(value == null)
        
            continue;
        

        for(int j=i+1; j<totalSize; j++)
        
            if(value.equals(inputArray[j]))
            
                inputArray[j] = null;
            
        
        resultArray[newSize++] = value;
    

    long endTime = System.nanoTime()-startTime;
    System.out.println("Total Time-B:"+endTime);
    return resultArray;

【讨论】:

为什么你要发布一个已经有 2 年历史的线性和对数线性解决方案的问题的二次解决方案,也更简单?

以上是关于如何从 ArrayList 中删除重复的元素?的主要内容,如果未能解决你的问题,请参考以下文章

如果元素在arraylist中重复,如何删除所有出现的元素[重复]

如何使用原始元素删除 ArrayList 中的重复值[重复]

从 ArrayList 的 ArrayList 中删除重复项 [重复]

从ArrayLists的ArrayList中删除重复项[重复]

如何从java arraylist中删除重复的对象[重复]

JAVA中如何从ArrayList中删除重复对象