Java中关于Map对象中改变Key值的问题

时间:2022-06-10 19:17:32

首先给出经验:通常情况下都是将Map的key设为不可变量,如string等,不要用可变量做key。

以下是我的教训!


import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class TestMap {
public static void main(String[] args) {

List<Integer> key1 = new LinkedList<>();
key1.add(1);

Set<Integer> value1 = new HashSet<>();
value1.add(100);

//分别用List和Set做key和value
Map<List<Integer>, Set<Integer>> map = new HashMap<>();
map.put(key1, value1);

System.out.println(map.get(key1));//①

key1.add(2);
System.out.println(key1);//②

Set<Integer> coll = map.get(key1);//③
coll.remove(100);

}
}

在①处能够正常取得value1,输出[100];在②处修改了key1,key1变为[1,2],再次取value1,发现coll为null。经调试,确定map中的内容是{[1, 2]=[100]},为什么取不到东西呢?


看源码:

map的get方法源码如下:

    public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
该方法首先调用了hash(key)方法。

hash(key)的源码如下:

   static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
因为在这里key的实际类型为LinkedList<Integer>,在我的这个1.8版本的JDK中,LinkedList源码没有重写quals方法和hashCode方法,一直往回推,可以看到其祖先类AbstractList中重写了quals方法和hashCode方法,其中hashCode方法源码如下:
    public int hashCode() {
int hashCode = 1;
for (E e : this)
hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
return hashCode;
}
debug到这里会发现确实得到了一个哈希码,于是hash(key)也就返回了一个int值。接着回到get方法,在里面调用了getNode方法,源码如下:

    final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
if ((e = first.next) != null) {
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}

debug到这里,发现不满足if的判断条件,因此返回了null。这个方法到底用了什么策略我不想去深究。


按常理,理应能够取得value的,这里却得到null,可能JDK设计人员有特殊的考虑吧,所以千万不要用可变量做键值啊!!!