HashSet不删除现有元素

Ema*_*sid 2 java hash equals set

我有一个类输出,它基本上包含一个带有覆盖hashCode和equals的BitSet.然后我有一个输出的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);
Run Code Online (Sandbox Code Playgroud)

如果我做打印(输出)我得到

[Output@5a1, Output@5a3]
Run Code Online (Sandbox Code Playgroud)

现在,如果我这样做

o2.flip(1);

我明白了

[Output@5a3, Output@5a3]
Run Code Online (Sandbox Code Playgroud)

当然,这是Set的正常行为,因为Set无法知道元素的哈希码已经改变.

如果我现在这样做

outputs.remove(o1);
Run Code Online (Sandbox Code Playgroud)

我明白了

[Output@5a3]
Run Code Online (Sandbox Code Playgroud)

完善!

但如果我再做一次

outputs.remove(o1); //or outputs.remove(o2);
Run Code Online (Sandbox Code Playgroud)

它又回来了false,我还有[Output@5a3]

如果我这样做,这很奇怪

outputs.contains(o1) -> false
Run Code Online (Sandbox Code Playgroud)

这可以解释删除行为,尽管我不明白为什么它返回false,因为如果我这样做

    for(Output o : outputs) {
        System.out.println(o.equals(o1));
    }
Run Code Online (Sandbox Code Playgroud)

它输出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));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

[Output@5a1, Output@5a3]
[Output@5a3, Output@5a3]
[Output@5a3]
[Output@5a3]
true
Run Code Online (Sandbox Code Playgroud)

Era*_*ran 8

当你改变a HashSet(或a中的一个键HashMap)的hashCode元素时,该元素的元素可能会改变(在你的例子中,hashCode取决于你改变hashCodeBitSet成员的元素).

但是,HashSet不知道该更改,因此不会将元素移动到与新对应的bin中hashCode.

因此,当您搜索该元素时,将使用new执行搜索hashCode(HashSet搜索始终以hashCode- 仅在找到包含具有该元素的所有元素的bin之后hashCode,equals()用于查找正确的元素),并且它失败,因为元素仍然位于与原始文件匹配的文件夹中hashCode.

这就是改变一个元素的一个坏主意的原因HashSet.