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 开头 - 只有在找到包含所有具有 hashCodeequals() 的元素都用于查找正确的元素),但它失败了,因为该元素仍然位于与原始 hashCode.[=25 相匹配的 bin 中=]

这就是改变 HashSet 的元素不是一个好主意的原因。