在Collection中查找重复条目

时间:2012-05-25 13:53:06

标签: java collections duplicates equality

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


要明确自己:我想根据具体标准比较条目。所以我认为Predicate只返回truefalse是不够的。


我无法使用equals

7 个答案:

答案 0 :(得分:7)

这取决于标准的语义:

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

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

答案 1 :(得分:4)

如果你想找到重复项,而不是仅删除它们,一种方法是将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()); 

答案 2 :(得分:3)

您可以调整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<Person> people = new ArrayList<Person>();
        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<Object> seen = new HashSet<Object>();
        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]上播放此示例。

答案 3 :(得分:2)

你可以使用一个地图,当迭代集合时,将元素放入地图(谓词将形成键),如果已经有一个条目,你就会发现重复。

有关详细信息,请参阅此处:Finding duplicates in a collection

答案 4 :(得分:2)

我在IEqualityComparer<T>创建了一个类似于.NET界面的新界面。

这样的EqualityComparator<T>然后传递给下面检测重复的方法。

public static <T> boolean hasDuplicates(Collection<T> collection,
        EqualsComparator<T> equalsComparator) {
    List<T> 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;
}

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

答案 5 :(得分:0)

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

Set uniqueItems = new TreeSet<>(yourComparator);
List<?> duplicates = objects.stream().filter(o -> !uniqueItems.add(o)).collect(Collectors.toList());
调用yourComarator时会使用

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

请注意,根据the TreeSet documentation,项目的equals方法必须与yourComarator一致才能生效。

答案 6 :(得分:-2)

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