[英]HashSet not removing existing element
I have a class Output which basically contains a BitSet with overides on hashCode and equals. 我有一个类输出,它基本上包含一个带有覆盖hashCode和equals的BitSet。 Then I have a HashSet of Outputs and I do the following operations :
然后我有一个输出的HashSet,我做了以下操作:
Set<Output> outputs = new HashSet<>();
Output o1 = new Output();
o1.flip(3);
Output o2 = new Output();
o2.flip(1);
o2.flip(3);
outputs.add(o1);
outputs.add(o2);
If I do a print(outputs) I get 如果我做打印(输出)我得到
[Output@5a1, Output@5a3]
Now if I do 现在,如果我这样做
o2.flip(1);
I get 我明白了
[Output@5a3, Output@5a3]
which of course, is the normal behavior of a Set, since the Set can't be aware that the hashcode of an element has changed. 当然,这是Set的正常行为,因为Set无法知道元素的哈希码已经改变。
If I now do 如果我现在这样做
outputs.remove(o1);
I get 我明白了
[Output@5a3]
Perfect! 完善!
But if I do it again 但如果我再做一次
outputs.remove(o1); //or outputs.remove(o2);
it returns false
and I still have [Output@5a3]
它返回
false
,我仍然有[Output@5a3]
And this is strange since if I do 如果我这样做,这很奇怪
outputs.contains(o1) -> false
which may explain the remove behavior, altough I don't understand why it returns false since if I do 这可以解释删除行为,尽管我不明白为什么它返回false,因为如果我这样做
for(Output o : outputs) {
System.out.println(o.equals(o1));
}
It outputs true
. 它输出
true
。
Any ideas why this happens? 任何想法为什么会这样?
The complete code: 完整的代码:
class Output {
BitSet values;
public Output() {
values = new BitSet(4);
}
public void flip(int index) {
values.flip(index);
}
public int hashCode() {
int hash = 3;
hash = 67 * hash + Objects.hashCode(this.values);
return hash;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Output)) {
return false;
}
Output other = (Output) obj;
return this.values.equals(other.values);
}
}
public class Main {
public static void main(String args[]) {
Set<Output> outputs = new HashSet<>();
Output o1 = new Output();
o1.flip(3);
Output o2 = new Output();
o2.flip(1);
o2.flip(3);
outputs.add(o1);
outputs.add(o2);
System.out.println(outputs);
o2.flip(1);
System.out.println(outputs);
outputs.remove(o1);
System.out.println(outputs);
outputs.remove(o1);
System.out.println(outputs);
for (Output o : outputs) {
System.out.println(o.equals(o1));
}
}
}
Output: 输出:
[Output@5a1, Output@5a3]
[Output@5a3, Output@5a3]
[Output@5a3]
[Output@5a3]
true
When you mutate an element of a HashSet
(or a key in a HashMap
), the hashCode
of that element may change (and in your example, the hashCode
depends on the hashCode
of the BitSet
member, which you changed). 当你改变
HashSet
一个元素(或HashMap
一个键)时,该元素的hashCode
可能会改变(在你的例子中, hashCode
依赖于你改变的BitSet
成员的hashCode
)。
However, the HashSet
is not aware of that change, and therefore doesn't move the element to the bin corresponding with the new hashCode
. 但是,
HashSet
不知道该更改,因此不会将元素移动到与新hashCode
对应的bin。
Therefore, when you search for that element, the search is performed using the new hashCode
( HashSet
search always begins with the hashCode
- only after finding the bin that contains all the elements having that hashCode
, equals()
is used to find the right element), and it fails, since the element is still located in the bin matching the original hashCode
. 因此,当您搜索该元素时,使用新的
hashCode
执行搜索( HashSet
搜索始终以hashCode
开头 - 仅在找到包含具有该hashCode
所有元素的bin之后,使用equals()
来查找正确的元素),它失败了,因为元素仍然位于与原始hashCode
匹配的bin中。
That's the reason it is a bad idea to mutate elements of a HashSet
. 这就是改变
HashSet
元素的一个坏主意的原因。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.