更新Java HashMap密钥

时间:2011-07-20 19:28:41

标签: java hashmap equals hashcode

我只是想知道,如果HashMap的密钥是可变的会发生什么,下面的测试程序会证明这一点,并且我无法理解何时equals和hashCode方法返回 真实且相同的值,为什么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;
    }
} 

这是输出:

  

真   假6 6真

5 个答案:

答案 0 :(得分:14)

javadoc解释了

  

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

基本上,不要在地图中使用可变对象作为键,否则会被烧毁

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

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

答案 1 :(得分:9)

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

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

显然,如何将事情放入存储桶中比这更复杂,但这就是核心工作方式。

答案 2 :(得分:6)

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

那么不要那样做。

答案 3 :(得分:0)

当您第一次放置“m1”时,hashCode()为5.因此HashMap使用5将值放入相应的存储桶中。更改m2后,hashCode()为6,所以当您尝试查找您输入的值时,它所查看的存储桶就不同了。

答案 4 :(得分:0)

伴随ptomli回答的代码示例。

import java.util.*;

class Elem {
    private int n;

    public Elem(int n) {
        this.n = n;
    }

    public void setN(int n) {
        this.n = n;
    }

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

    @Override
    public boolean equals(Object e) {
        if (this == e)
            return true;
        if (!(e instanceof Elem))
            return false;
        Elem an = (Elem) e;
        return n == an.n;
    }
}

public class MapTest {
    public static void main (String [] args)  {
        Elem e1 = new Elem(1);
        Elem e2 = new Elem(2);

        HashMap map = new HashMap();
        map.put(e1, 100);
        map.put(e2, 200);

        System.out.println("before modification: " + map.get(e1));  
        e1.setN(9);
        System.out.println("after modification using updated key: " + map.get(e1)); 

        Elem e3 = new Elem(1);
        System.out.println("after modification using key which equals to the original key: " + map.get(e3));    
    }
}

编译并运行它。结果是:

before modification: 100
after modification using updated key: null
after modification using key which equals to the original key: null

我在Linux上使用Java 6.