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

574
我有一个 ArrayList<String>,我想从中删除重复的字符串。如何做到这一点?
40个回答

1094

如果你不想在集合中出现重复元素,那么你应该考虑为什么要使用允许重复元素的集合。最简单的去重方式是将集合内容添加到一个Set(不允许重复)中,然后再将Set添加回ArrayList中:

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

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


291
如果你希望保留顺序,请参考 LinkedHashSet。 - volley
3
要在O(n)的时间复杂度内从ArrayList中找到所有重复项,重要的是在列表中拥有正确定义的equals方法的对象(对于数字没有问题):public Set findDuplicates(List list) { Set items = new HashSet(); Set duplicates = new HashSet(); for (Object item : list) { if (items.contains(item)) { duplicates.add(item); } else { items.add(item); } } return duplicates; } - Ondrej Bozek
6
一个好的实践是使用接口类型ListSet来定义变量(而不是像你的例子中使用实现类型ArrayListHashSet)。 - Jonik
35
你可以使用new HashSet(al)来清理代码,而不是初始化为空并调用addAll - ashes999
2
如果您使用TreeSet而不是HashSet,您可以定义自己的比较器来使用,TreeSet将考虑两个项目是否重复取决于比较器.compare(e1, e2)返回0。请注意,这将破坏ArrayList的现有顺序。 - Jarred Allen
显示剩余11条评论

320

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

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

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


12
LinkedHashSet是否有保证会保留列表中的哪些重复项呢?例如,如果原始列表中的位置1、3和5是重复的,我们可以假设这个过程会删除3和5吗?还是可能删除1和3?谢谢。 - Matt Briançon
18
@Matt:是的,它确保了这一点。文档中写道:“这个链表定义了迭代顺序,即元素插入集合的顺序(插入顺序)。请注意,如果元素重新插入到集合中,插入顺序不会受到影响。” - abahgat
非常有趣。我这里有一个不同的情况。我不是在尝试对字符串进行排序,而是另一个名为AwardYearSource的对象。这个类有一个名为year的int属性。因此,我想根据年份删除重复项。即如果有2010年的年份被提到超过一次,我想要删除那个AwardYearSource对象。我该怎么做? - WowBow
例如,您可以定义一个包装器对象,其中包含AwardYearSource。并且基于AwardYearSources年份字段定义此包装器对象的equals方法。然后,您可以使用这些包装器对象的Set。 - Ondrej Bozek
@WowBow 或实现 Comparable/Comparator - shrini1000

175
在Java 8中:
List<String> deduped = list.stream().distinct().collect(Collectors.toList());

请注意,对于列表成员,hashCode-equals合同应该得到尊重,以使过滤正常工作。

1
我怎么才能做到不区分大小写且只返回不同的结果? - StackFlowed
@StackFlowed 如果您不需要保留列表的顺序,可以将其添加到new TreeSet<String>(String.CASE_INSENSITIVE_ORDER)中。第一个添加的元素将保留在集合中,因此如果您的列表包含“Dog”和“dog”(按照该顺序),则TreeSet将包含“Dog”。如果必须保留顺序,则在答案行之前放置list.replaceAll(String::toUpperCase); - Paul
1
我遇到了这个错误:不兼容的类型:List<Object> 无法转换为 List<String>。 - Samir
这通常是一个简单的解决方案,但是如何从int[]的Arraylist中删除重复项呢? - Laser Infinite

76
假设我们有一个类似的字符串列表: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。
使用 Guava
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以获取更多信息。

当我输入之前的评论时,我认为“并行流”总是会提供更好的性能。但这是一个谬论。后来我了解到,在某些情况下应该使用并行流。在这种情况下,并行流不会提供任何更好的性能。是的,并行流在某些情况下可能无法产生期望的结果。List<String> deDupStringList3 = stringList.stream().map(String::toLowerCase).distinct().collect(Collectors.toList()); 在这种情况下应该是合适的解决方案。 - Diablo

54
如果您不想要重复的元素,请使用 Set,而非 List。您可以使用以下代码将 List 转换为 Set:
// list is some List of Strings
Set<String> s = new HashSet<String>(list);

如果确实有必要,您可以使用相同的构造方法将 Set 转换为 List


同样地,在该线程的底部,我给出了一个答案,其中我使用Set来处理自定义对象。如果有人有自定义对象,例如“联系人”或“学生”,可以使用该答案,对我来说效果很好。 - Muhammad Adil
问题出现在你必须特别访问一个元素时。例如,在Android中将对象绑定到列表项视图时,会给出其索引。因此,这里不能使用Set - TheRealChx101
当列表是对象列表时,我该如何处理它? - jvargas

30

这里有一种方法,不会影响你的列表顺序:

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
这个答案有两个缺点:1)它没有使用泛型,而是使用了原始类型(应该使用ArrayList<T>而不是ArrayList);2)可以通过使用for (T current : l1) { ... }来避免显式迭代器的创建。即使你想要显式使用一个迭代器,iterador也拼错了。 - randers
9
与链式哈希集实现的线性时间相比,这个实现需要二次时间。例如,在具有10个元素的列表上,它需要比链式哈希集实现长10倍,在具有10,000个元素的列表上需要比链式哈希集实现长10,000倍。JDK 6 ArrayList.contains 的实现方式与JDK8相同。 - Patrick M

30

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

 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());

如何从ArrayList中删除重复元素


30

您也可以这样做,并保留顺序:

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

我认为这是从ArrayList中删除重复项的最佳方法。绝对值得推荐。感谢@Nenad的答案。 - ByWaleed

27

这可以解决问题:

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;
}

1
我更喜欢这个解决方案。 - Tushar Gogna

21

2
请注意,有一个ImmutableSet.asList()方法,返回一个ImmutableList,如果你需要将其作为List返回。 - Andy Turner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接