在Collection中查找重复条目

是否有工具或库可根据可实施的特定标准在集合中查找重复条目?


为了使自己清楚:我想根据具体标准比较条目。 所以我认为Predicate返回truefalse是不够的。


我不能使用equals

这取决于标准的语义:

如果您的标准对于给定的类始终是相同的,并且是基础概念固有的 ,那么您应该只实现equalshashCode并使用集合。

如果您的标准取决于上下文 , org.apache.commons.collections.CollectionUtils.select(java.util.Collection,org.apache.commons.collections.Predicate)可能是适合您的解决方案。

如果要查找重复项,而不是仅仅删除它们,一种方法是将Collection抛出到数组中,通过实现条件的Comparator对数组进行排序,然后线性遍历数组,查找相邻的重复项。

这是一个草图(未经测试):

  MyComparator myComparator = new MyComparator(); MyType[] myArray = myList.toArray(); Arrays.sort( myArray, myComparator ); for ( int i = 1; i < myArray.length; ++i ) { if ( 0 == myComparator.compare( myArray[i - 1], myArray[i] )) { // Found a duplicate! } } 

编辑:从您的评论中,您只想知道是否有重复项。 上面的方法也适用于此。 但是你可以更简单地用自定义Comparator创建一个java.util.SortedSet。 这是一个草图:

  MyComparator myComparator = new MyComparator(); TreeSet treeSet = new TreeSet( myComparator ); treeSet.addAll( myCollection ); boolean containsDuplicates = (treeSet.size() != myCollection.size()); 

您可以调整Java集以在任意类型的对象之间搜索重复项:将目标类包装在私有包装器中,该包装器根据您的条件评估相等性,并构造一组包装器。

这是一个有点冗长的例子,说明了这种技术。 它认为具有相同名字的两个人是相等的,因此它在五个对象的数组中检测到三个重复。

 import java.util.*; import java.lang.*; class Main { static class Person { private String first; private String last; public String getFirst() {return first;} public String getLast() {return last;} public Person(String f, String l) { first = f; last = l; } public String toString() { return first+" "+last; } } public static void main (String[] args) throws java.lang.Exception { List people = new ArrayList(); people.add(new Person("John", "Smith")); people.add(new Person("John", "Scott")); people.add(new Person("Jack", "First")); people.add(new Person("John", "Walker")); people.add(new Person("Jack", "Black")); Set seen = new HashSet(); for (Person p : people) { final Person thisPerson = p; class Wrap { public int hashCode() { return thisPerson.getFirst().hashCode(); } public boolean equals(Object o) { Wrap other = (Wrap)o; return other.wrapped().getFirst().equals(thisPerson.getFirst()); } public Person wrapped() { return thisPerson; } }; Wrap wrap = new Wrap(); if (seen.add(wrap)) { System.out.println(p + " is new"); } else { System.out.println(p + " is a duplicate"); } } } } 

你可以在ideone [link]上玩这个例子。

您可以使用地图并在迭代集合时将元素放入地图(谓词将形成键),如果已有条目,则您发现了重复。

有关详细信息,请参阅此处: 查找集合中的重复项

我创建了一个类似于.NET中的IEqualityComparer接口的新接口。

然后,这样的EqualityComparator I传递给检测重复的以下方法。

 public static  boolean hasDuplicates(Collection collection, EqualsComparator equalsComparator) { List list = new ArrayList<>(collection); for (int i = 0; i < list.size(); i++) { T object1 = list.get(i); for (int j = (i + 1); j < list.size(); j++) { T object2 = list.get(j); if (object1 == object2 || equalsComparator.equals(object1, object2)) { return true; } } } return false; } 

这样我就可以根据自己的需要定制比较。

Treeset允许您轻松完成此操作:

 Set uniqueItems = new TreeSet<>(yourComparator); List duplicates = objects.stream().filter(o -> !uniqueItems.add(o)).collect(Collectors.toList()); 

调用uniqueItems.add(o)时会使用uniqueItems.add(o) ,它会将项目添加到集合中,如果项目是唯一的,则返回true 。 如果比较器认为该项是重复的,则add(o)将返回false。

请注意,根据TreeSet文档 ,项目的equals方法必须与yourComarator保持一致yourComarator

迭代包含重复项的ArrayList并将它们添加到HashSet 。 当add方法在HashSet返回false时,只需将副本记录到控制台即可。