HashSet 不删除现有元素
HashSet not removing existing element
我有一个 class 输出,它基本上包含一个 BitSet,上面覆盖了 hashCode 和 equals。然后我有一个 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);
如果我打印(输出)我得到
[Output@5a1, Output@5a3]
现在如果我这样做
o2.flip(1);
我明白了
[Output@5a3, Output@5a3]
这当然是 Set 的正常行为,因为 Set 无法意识到元素的哈希码已更改。
如果我现在做
outputs.remove(o1);
我明白了
[Output@5a3]
完美!
但如果我再做一次
outputs.remove(o1); //or outputs.remove(o2);
它 returns false
我还有 [Output@5a3]
这很奇怪,因为如果我这样做
outputs.contains(o1) -> false
这可以解释删除行为,尽管我不明白为什么它 returns 是假的,因为如果我这样做
for(Output o : outputs) {
System.out.println(o.equals(o1));
}
输出true
.
知道为什么会这样吗?
完整代码:
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@5a1, Output@5a3]
[Output@5a3, Output@5a3]
[Output@5a3]
[Output@5a3]
true
当您改变 HashSet
的元素(或 HashMap
中的键)时,该元素的 hashCode
可能会改变(在您的示例中,hashCode
取决于您更改的 BitSet
成员的 hashCode
)。
但是,HashSet
不知道该更改,因此不会将元素移动到与新 hashCode
对应的容器中。
因此,当您搜索该元素时,将使用新的 hashCode
执行搜索(HashSet
搜索始终以 hashCode
开头 - 只有在找到包含所有具有 hashCode
、equals()
的元素都用于查找正确的元素),但它失败了,因为该元素仍然位于与原始 hashCode
.[=25 相匹配的 bin 中=]
这就是改变 HashSet
的元素不是一个好主意的原因。
我有一个 class 输出,它基本上包含一个 BitSet,上面覆盖了 hashCode 和 equals。然后我有一个 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);
如果我打印(输出)我得到
[Output@5a1, Output@5a3]
现在如果我这样做
o2.flip(1);
我明白了
[Output@5a3, Output@5a3]
这当然是 Set 的正常行为,因为 Set 无法意识到元素的哈希码已更改。
如果我现在做
outputs.remove(o1);
我明白了
[Output@5a3]
完美!
但如果我再做一次
outputs.remove(o1); //or outputs.remove(o2);
它 returns false
我还有 [Output@5a3]
这很奇怪,因为如果我这样做
outputs.contains(o1) -> false
这可以解释删除行为,尽管我不明白为什么它 returns 是假的,因为如果我这样做
for(Output o : outputs) {
System.out.println(o.equals(o1));
}
输出true
.
知道为什么会这样吗?
完整代码:
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@5a1, Output@5a3]
[Output@5a3, Output@5a3]
[Output@5a3]
[Output@5a3]
true
当您改变 HashSet
的元素(或 HashMap
中的键)时,该元素的 hashCode
可能会改变(在您的示例中,hashCode
取决于您更改的 BitSet
成员的 hashCode
)。
但是,HashSet
不知道该更改,因此不会将元素移动到与新 hashCode
对应的容器中。
因此,当您搜索该元素时,将使用新的 hashCode
执行搜索(HashSet
搜索始终以 hashCode
开头 - 只有在找到包含所有具有 hashCode
、equals()
的元素都用于查找正确的元素),但它失败了,因为该元素仍然位于与原始 hashCode
.[=25 相匹配的 bin 中=]
这就是改变 HashSet
的元素不是一个好主意的原因。