Answers:
firstSet.equals(secondSet)
这实际上取决于您要在比较逻辑中执行的操作……即,如果您在一个集合中找到一个元素而不在另一个集合中找到一个元素,会发生什么?您的方法具有void
返回类型,因此我假设您将在此方法中做必要的工作。
如果需要,可以进行更细粒度的控制:
if (!firstSet.containsAll(secondSet)) {
// do something if needs be
}
if (!secondSet.containsAll(firstSet)) {
// do something if needs be
}
如果您需要获取一组中的元素而不是另一组中的元素。
编辑:set.removeAll(otherSet)
返回布尔值,而不是集合。要使用removeAll(),您必须复制该集合然后使用它。
Set one = new HashSet<>(firstSet);
Set two = new HashSet<>(secondSet);
one.removeAll(secondSet);
two.removeAll(firstSet);
如果内容one
和two
都是空的,那么你知道这两组都是平等的。如果不是,那么您就有使集合不相等的元素。
您提到记录的数量可能很高。如果基础实现是a,HashSet
那么每个记录的获取都将O(1)
及时完成,因此您无法真正做到比这更好。TreeSet
是O(log n)
。
equals
快于两次调用containsAll
。看我的答案。
如果只想知道集合是否相等,则equals
on方法AbstractSet
大致实现如下:
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Set))
return false;
Collection c = (Collection) o;
if (c.size() != size())
return false;
return containsAll(c);
}
注意它如何优化以下常见情况:
之后,只要在另一个集合中找到一个不在该集合中的元素,containsAll(...)
就会返回false
。但是,如果两个集合中都存在所有元素,则需要测试所有元素。
因此,当两组相同但不相同的对象时,发生最坏情况的性能。该费用通常等于O(N)
或O(NlogN)
取决于的实现this.containsAll(c)
。
如果集合很大并且仅在很小比例的元素上有差异,那么您将获得最差的案例性能。
更新
如果您愿意花时间在定制集实现上,则可以采用一种方法来改善“几乎相同”的情况。
这个想法是,您需要为整个集合预先计算并缓存一个哈希,以便可以在中获取集合的当前哈希码值O(1)
。然后,您可以比较两个集合的哈希码作为加速。
您如何实现这样的哈希码?好吧,如果设置的哈希码是:
那么您每次添加或删除元素时都可以廉价地更新集合的缓存哈希码。在这两种情况下,您只需将元素的哈希码与当前设置的哈希码进行异或。
当然,这假设元素哈希码是稳定的,而元素是集合的成员。它还假定元素类的哈希码函数具有良好的扩展性。这是因为,当两个设置的哈希码相同时,您仍然必须退回O(N)
所有元素的比较。
至少从理论上讲,您可以进一步推广这个想法。
警告 -这是高度投机的。如果您愿意,可以进行“思想实验”。
假设您的set元素类具有一种返回该元素的加密校验和的方法。现在,通过对元素返回的校验和进行XOR来实现集合的校验和。
这能给我们带来什么?
好吧,如果我们假设没有发生任何意外情况,则任何两个不相等的集合元素具有相同的N位校验和的概率为2 -N。2个不等集具有相同N位校验和的概率也为2 -N。所以我的想法是,您可以实现equals
为:
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Set))
return false;
Collection c = (Collection) o;
if (c.size() != size())
return false;
return checksums.equals(c.checksums);
}
在上述假设下,这只会在2 -N次内给您一次错误的答案。如果您使N足够大(例如512位),那么错误答案的可能性就可以忽略不计(例如大约10 -150)。
缺点是计算元素的加密校验和非常昂贵,尤其是随着位数的增加。因此,您确实需要一种有效的机制来记住校验和。那可能是有问题的。
另一个缺点是,无论概率多么小,错误概率都可能为零,这是不可接受的。(但是如果是这样的话……您如何处理宇宙射线翻转临界位的情况?或者在冗余系统的两个实例中它同时翻转相同的位?)
您可以从https://www.mkyong.com/java/java-how-to-compare-two-sets/获得以下解决方案
public static boolean equals(Set<?> set1, Set<?> set2){
if(set1 == null || set2 ==null){
return false;
}
if(set1.size() != set2.size()){
return false;
}
return set1.containsAll(set2);
}
或者,如果您更喜欢使用单个return语句:
public static boolean equals(Set<?> set1, Set<?> set2){
return set1 != null
&& set2 != null
&& set1.size() == set2.size()
&& set1.containsAll(set2);
}
对于以下特定情况,有一个O(N)解决方案:
下面的代码假定两组都基于可比较的记录。类似的方法可以基于比较器。
public class SortedSetComparitor <Foo extends Comparable<Foo>>
implements Comparator<SortedSet<Foo>> {
@Override
public int compare( SortedSet<Foo> arg0, SortedSet<Foo> arg1 ) {
Iterator<Foo> otherRecords = arg1.iterator();
for (Foo thisRecord : arg0) {
// Shorter sets sort first.
if (!otherRecords.hasNext()) return 1;
int comparison = thisRecord.compareTo(otherRecords.next());
if (comparison != 0) return comparison;
}
// Shorter sets sort first
if (otherRecords.hasNext()) return -1;
else return 0;
}
}
我将在比较之前将secondSet放入HashMap中。这样,您可以将第二个列表的搜索时间减少到n(1)。像这样:
HashMap<Integer,Record> hm = new HashMap<Integer,Record>(secondSet.size());
int i = 0;
for(Record secondRecord : secondSet){
hm.put(i,secondRecord);
i++;
}
for(Record firstRecord : firstSet){
for(int i=0; i<secondSet.size(); i++){
//use hm for comparison
}
}
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Set))
return false;
Set<String> a = this;
Set<String> b = o;
Set<String> thedifference_a_b = new HashSet<String>(a);
thedifference_a_b.removeAll(b);
if(thedifference_a_b.isEmpty() == false) return false;
Set<String> thedifference_b_a = new HashSet<String>(b);
thedifference_b_a.removeAll(a);
if(thedifference_b_a.isEmpty() == false) return false;
return true;
}
我认为可以使用带有equals方法的方法引用。我们假定毫无疑问的对象类型具有其自己的比较方法。简单明了的例子在这里,
Set<String> set = new HashSet<>();
set.addAll(Arrays.asList("leo","bale","hanks"));
Set<String> set2 = new HashSet<>();
set2.addAll(Arrays.asList("hanks","leo","bale"));
Predicate<Set> pred = set::equals;
boolean result = pred.test(set2);
System.out.println(result); // true
set.equals(set2)