我只是想知道,如果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真
答案 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.