我正在尝试优化一段比较列表元素的代码。
例如
public void compare(Set<Record> firstSet, Set<Record> secondSet){
for(Record firstRecord : firstSet){
for(Record secondRecord : secondSet){
// comparing logic
}
}
}
请注意,套装中的记录数量会很高。
由于
谢加
答案 0 :(得分:135)
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
的内容都为空,那么您就知道这两个集合是相同的。如果没有,那么你就有了使这些集不相等的元素。
您提到记录数量可能很高。如果底层实现是HashSet
,则每个记录的提取都在O(1)
时间内完成,因此您无法真正获得更好的结果。 TreeSet
为O(log n)
。
答案 1 :(得分:57)
如果您只是想知道这些集是否相等,equals
上的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)
中得到集合的当前哈希码值。然后,您可以将两组的哈希码作为加速度进行比较。
你怎么能实现这样的哈希码?好吧,如果设置的哈希码是:
然后,每次添加或删除元素时,您都可以便宜地更新集合的缓存哈希码。在这两种情况下,您只需使用当前设置的哈希码对元素的哈希码进行异或。
当然,这假设元素哈希码是稳定的,而元素是集合的成员。它还假设元素类hashcode函数给出了良好的扩展。这是因为当两个设置的哈希码相同时,您仍然需要回退到所有元素的equals
比较。
你可以进一步理解这个想法......至少在理论上是这样。
假设您的set元素类有一个方法来返回元素的加密校验和。现在通过对为元素返回的校验和进行异或来实现集合的校验和。
这给我们带来了什么?
好吧,如果我们假设没有任何事情发生,那么任何两个不相等的集合元素具有相同的N比特校验和的概率是2 -N 。并且概率2不等集具有相同的N位校验和也是2 -N 。所以我的想法是你可以将 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);
}
实现为:
{{1}}
根据上述假设,这只会在2 -N 时间内给出错误答案。如果使N足够大(例如512位),则错误答案的概率可以忽略不计(例如大约10 -150 )。
缺点是计算元素的加密校验和非常昂贵,尤其是随着位数的增加。所以你真的需要一个有效的机制来记忆校验和。这可能会有问题。
答案 2 :(得分:15)
Guava Sets
中有一种方法可以在这里提供帮助:
public static <E> boolean equals(Set<? extends E> set1, Set<? extends E> set2){
return Sets.symmetricDifference(set1,set2).isEmpty();
}
答案 3 :(得分:4)
对于特定情况,有一个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;
}
}
答案 4 :(得分:4)
您从https://www.mkyong.com/java/java-how-to-compare-two-sets/
获得以下解决方案.menu-hover-style-2 .main-navigation-ul > li#menu-item-43.current-menu-item > a.menu-item-link, .main-navigation-ul > li#menu-item-43.current-menu-ancestor > a.menu-item-link {
color: #000000!important;
overflow: hidden;
position: relative;
}
.menu-hover-style-2 .main-navigation-ul > li#menu-item-43.current-menu-item > a.menu-item-link:before, .main-navigation-ul > li#menu-item-43.current-menu-ancestor > a.menu-item-link:before {
position: absolute;
background: radial-gradient(ellipse, transparent, transparent 4px, #f0b49e 4px, #f0b49e 6px, transparent 6px);
background-size: 20px 18px;
width: 200px;
height: 9px;
content: "";
top: 30px;
z-index: -1;
}
.menu-hover-style-2 .main-navigation-ul > li#menu-item-43.current-menu-item > a.menu-item-link:after, .main-navigation-ul > li#menu-item-43.current-menu-ancestor > a.menu-item-link:after {
position: absolute;
background: radial-gradient(ellipse, transparent, transparent 4px, #f0b49e 4px, #f0b49e 6px, transparent 6px);
background-size: 20px 18px;
width: 200px;
height: 9px;
top: 39px;
left: 2px;
background-position: 8px -10px;
content: "";
z-index: -1;
}
或者,如果您希望使用单个return语句:
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);
}
答案 5 :(得分:3)
如果您使用Guava
库,则可以执行以下操作:
SetView<Record> added = Sets.difference(secondSet, firstSet);
SetView<Record> removed = Sets.difference(firstSet, secondSet);
然后根据这些得出结论。
答案 6 :(得分:2)
我会在比较之前将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
}
}
答案 7 :(得分:1)
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;
}
答案 8 :(得分:-1)
我认为可以使用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