元素存在,但'Set.contains(element)'返回 false

2022-09-03 00:00:41

元素如何不能包含在原始集合中,而是包含在其未修改的副本中?

原始集不包含元素,而其副本包含元素。见图。

以下方法返回 ,尽管它应始终返回 。和 的实现在这两种情况下都是 。truefalsecclustersHashSet

public static boolean confumbled(Set<String> c, Set<Set<String>> clusters) {
    return (!clusters.contains(c) && new HashSet<>(clusters).contains(c));
}

调试表明该元素包含在原始元素中,但由于某种原因返回。见图。Set.contains(element)false

有人可以向我解释发生了什么事吗?


答案 1

如果更改 中的元素(在您的情况下,元素是 ,因此添加或删除 String 将更改它们),则可能无法找到它,因为元素的 将不同于元素首次添加到 .SetSet<String>Set.contains(element)hashCodeHashSet

当您创建一个包含原始元素的新元素时,这些元素将根据其当前元素添加,因此对于新的 元素将返回 true。HashSethashCodeSet.contains(element)HashSet

应避免将可变实例放在 a 中(或将它们用作 a 中的键),如果无法避免,请确保在更改元素之前将其删除,然后在之后重新添加。否则你的将破碎。HashSetHashMapHashSet

例如:

Set<String> set = new HashSet<String>();
set.add("one");
set.add("two");
Set<Set<String>> setOfSets = new HashSet<Set<String>>();
setOfSets.add(set);
boolean found = setOfSets.contains(set); // returns true
set.add("three");
Set<Set<String>> newSetOfSets = new HashSet<Set<String>>(setOfSets);
found = setOfSets.contains(set); // returns false
found = newSetOfSets.contains(set); // returns true

答案 2

最常见的原因是元素或键在插入后被更改,导致基础数据结构损坏。

注意:当您将对 a 的引用添加到另一个引用时,您正在添加引用的副本,并且不会复制基础,并且如果您对其进行更改,这些更改会影响将其放入。Set<String>Set<Set<String>>Set<String>Set<Set<String>>

例如:

Set<String> s = new HashSet<>();
Set<Set<String>> ss = new HashSet<>();
ss.add(s);
assert ss.contains(s);

// altering the set after adding it corrupts the HashSet
s.add("Hi");
// there is a small chance it may still find it.
assert !ss.contains(s);

// build a correct structure by copying it.
Set<Set<String>> ss2 = new HashSet<>(ss);
assert ss2.contains(s);

s.add("There");
// not again.
assert !ss2.contains(s);