是否有工具或库可以根据可以实现的特定标准在集合中查找重复条目?
为了让自己清楚:我想根据特定标准将条目相互比较。所以我认为Predicate
回归只是true
或false
不够。
我不能用equals
。
是否有工具或库可以根据可以实现的特定标准在集合中查找重复条目?
为了让自己清楚:我想根据特定标准将条目相互比较。所以我认为Predicate
回归只是true
或false
不够。
我不能用equals
。
它取决于标准的语义:
如果你的标准对于给定的类总是相同的,并且是底层概念所固有的,你应该只实现equals
和hashCode
使用一个集合。
如果您的标准取决于 context, org.apache.commons.collections.CollectionUtils.select(java.util.Collection, org.apache.commons.collections.Predicate)可能是适合您的解决方案。
如果您想查找重复项,而不仅仅是删除它们,一种方法是将集合放入数组中,通过实现您的标准的 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!
}
}
编辑:从您的评论中,您只想知道是否有重复。上面的方法也适用于此。但是您可以更简单地创建一个带有自定义比较器的 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<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]上玩这个例子。
您可以使用地图,并在遍历集合时将元素放入地图中(谓词将形成键),如果已经有一个条目,则您发现了重复项。
有关更多信息,请参见此处:在集合中查找重复项
我创建了一个类似于.NETIEqualityComparer<T>
中的接口的新接口。
然后我将这样的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;
}
这样我就可以根据自己的需要自定义比较。
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。
请注意,该项目的equals
方法必须与 TreeSet 文档一致yourComarator
才能正常工作。
迭代ArrayList
其中包含重复项并将它们添加到HashSet
. 当 add 方法返回 false 时,HashSet
只需将副本记录到控制台。