简体   繁体   English

HashSet不删除现有元素

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM