Java 在集合中查找重复项

Java 在集合中查找重复项,java,collections,duplicates,equality,Java,Collections,Duplicates,Equality,是否有工具或库可以根据可以实现的特定条件查找集合中的重复条目 让我自己清楚一点:我想根据特定的标准相互比较条目。所以我认为一个谓词只返回true或false是不够的 我不能使用equals您可以使用映射,在对集合进行迭代时,将元素放入映射中(谓词将形成键),如果已经存在一个条目,则可以找到重复项 有关更多信息,请参见此处:它取决于标准的语义: 如果给定类的标准总是相同的,并且是基础概念所固有的,那么您应该只实现等于和hashCode,并使用一个集合 如果您的标准取决于上下文,则可能是适合您的

是否有工具或库可以根据可以实现的特定条件查找集合中的重复条目



让我自己清楚一点:我想根据特定的标准相互比较条目。所以我认为一个
谓词
只返回
true
false
是不够的



我不能使用
equals

您可以使用映射,在对集合进行迭代时,将元素放入映射中(谓词将形成键),如果已经存在一个条目,则可以找到重复项


有关更多信息,请参见此处:

它取决于标准的语义:

如果给定类的标准总是相同的,并且是基础概念所固有的,那么您应该只实现
等于
hashCode
,并使用一个集合

如果您的标准取决于上下文,则可能是适合您的解决方案。

如果您希望查找重复项,而不是仅删除它们,一种方法是将集合放入数组,通过实现标准的比较器对数组排序,然后线性遍历数组,寻找相邻的副本

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;
}
这是一张草图(未经测试):


您可以调整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");
            }
        }
    }
}
import java.util.*;
导入java.lang.*;
班长{
静态类人{
私有字符串优先;
私有字符串最后;
公共字符串getFirst(){return first;}
公共字符串getLast(){return last;}
公众人物(字符串f、字符串l){
第一个=f;
last=l;
}
公共字符串toString(){
返回第一个+“”+最后一个;
}
}
公共静态void main(字符串[]args)引发java.lang.Exception{
List people=new ArrayList();
添加(新人物(“约翰”、“史密斯”);
添加(新人物(“约翰”、“斯科特”);
添加(新人物(“杰克”,“第一”));
添加(新人物(“约翰”、“沃克”);
添加(新人物(“杰克”、“黑人”);
Set seen=新的HashSet();
用于(人员p:人员){
最后一个人这个人=p;
类包装{
public int hashCode(){返回thisPerson.getFirst().hashCode();}
公共布尔等于(对象o){
包裹其他=(包裹)o;
返回other.wrapped().getFirst().equals(thissPerson.getFirst());
}
公共人物包装(){返回此人;}
};
换行=新换行();
如果(参见。添加(换行)){
System.out.println(p+“是新的”);
}否则{
System.out.println(p+“是一个副本”);
}
}
}
}

您可以在ideone上玩这个示例。

我创建了一个与中的接口类似的新接口

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

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;
}
public static boolean has duplicates(集合,
等比较器(等比较器){
列表=新的ArrayList(集合);
对于(int i=0;i

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

迭代包含重复项的
数组列表
,并将其添加到
哈希集。当add方法在
HashSet
中返回false时,只需将副本记录到控制台

Treeset允许您轻松地执行此操作:

Set uniqueItems = new TreeSet<>(yourComparator);
List<?> duplicates = objects.stream().filter(o -> !uniqueItems.add(o)).collect(Collectors.toList());
Set uniqueItems=新树集(您的比较器);
List duplicates=objects.stream().filter(o->!uniqueItems.add(o)).collect(Collectors.toList());
调用uniqueItems.add(o)
时使用
yourComarator
,它将项目添加到集合中,如果项目是唯一的,则返回
true
。如果比较器认为该项是重复项,
add(o)
将返回false


请注意,项目的
equals
方法必须与
yourComarator
一致,才能起作用。

您希望以何种方式指定重复数据消除标准?作为一个二元谓词?你想找到重复的还是删除它们?@andythonas-Cramer实际上,只要知道是否有重复就足够了。@New Talk-ah,然后,您可以将Samuel Rossille的答案产生的集合大小与原始集合的大小进行比较。@Andythonas Cramer请查看我编辑的问题。我想比较每个条目之间的大小,而不是任意条件。@dragon66如果您的哈希函数很好,效率与任何哈希表相同,每个项目都是
O(1)
,或者整个集合都是
O(N)
。dasblinkenlight:我有点担心wrap对象的创建,尽管我知道它们会超出循环。@dragon66 Java在创建小对象(这些对象很小)方面非常有效. 不幸的是,Java没有一个与.NET的相等比较器并行的概念——这将允许一个完全避免临时对象的解决方案。正如OP所说,他不能使用
equals()
。A
HashSet
Set uniqueItems = new TreeSet<>(yourComparator);
List<?> duplicates = objects.stream().filter(o -> !uniqueItems.add(o)).collect(Collectors.toList());