更新Java HashMap密钥

Abi*_*idi 10 java equals hashmap hashcode

我只是想知道,如果a的密钥HashMap是可变的,下面的测试程序会证明这一点会发生什么,而我无法理解当equals和hashCodemethods返回true和相同的值时,为什么会hashmap.containsKey返回false.

public class MutableKeyHashMap {

    public static void main(String []a){

            HashMap<Mutable, String> map = new HashMap<Mutable, String>();
            Mutable m1 = new Mutable(5);
            map.put(m1, "m1");
            Mutable m2 = new Mutable(5);
            System.out.println(map.containsKey(m2));    

            m2.setA(6);
            m1.setA(6);
            Mutable m3 = map.keySet().iterator().next();

            System.out.println(map.containsKey(m2)+"    "+m3.hashCode()+"       "+m2.hashCode()+"       "+m3.equals(m2));   

    }
}
class Mutable {

    int a;

    public Mutable(int a) {

        this.a = a;
    }

    @Override
    public boolean equals(Object obj) {

        Mutable m = (Mutable) obj;
        return m.a == this.a ? true : false; 
    }

    @Override
    public int hashCode(){
        return a;
    }

    public void setA(int a) {

        this.a = a;
    }

    public int getA() {
        return a;
    }
} 
Run Code Online (Sandbox Code Playgroud)

这个输出:

真假6 6真

pto*_*mli 14

javadoc的解释它

注意:如果将可变对象用作映射键,则必须非常小心.如果在对象是地图中的键的情况下以影响等于比较的方式更改对象的值,则不指定映射的行为.

基本上,不要将可变对象用作Map中的键,否则你会被烧毁

为了推断,因为文档可能看起来不清楚,我相信这里的相关点是"以影响equals的方式改变",并且你似乎假设每次调用contains时都会调用equals(Object).文档没有说明,措辞意味着他们可以被允许缓存计算.

看看源代码,似乎因为你的hashCode返回一个不同的值(5,现在是6),它可能会根据实现细节在另一个桶中查找.


Aff*_*ffe 10

你可以想到,如果这样,Map有16个桶.当你给它一个A == 5的对象时,它将它扔进桶5.现在你可以将A改为6,但它仍然在桶5中.地图不知道你改变了A,它没有重新排列东西内部.

现在你来了另一个A == 6的对象,你问Map是否有其中一个.它在第6桶中看起来并且说"不,没有." 它不会去为你检查所有其他桶.

显然,如何将事情放入桶中比这更复杂,但这就是它在核心的工作方式.


sta*_*lue 6

HashMap在哈希键的位置让你的对象5.然后将键更改为6并使用它containsKey来询问地图是否包含该对象.地图查看位置6并找不到任何内容,因此它会回答false.

所以不要那样做.