当我需要一个 Map 时,大多时候使用 HashMap。JDK还有一个 Map 实现是 IdentityHashMap 在某些场景发挥着重要作用。
从继承关系可以看到,IdentityHashMap 继承自 AbstractMap。实现了 Map 接口。
IdentityHashMap 与 HashMap 的一个重要区别,在 IdentityHashMap doc 中的第一句话就给出了答案。
This class implements the Map interface with a hash table, using reference-equality in place of object-equality when comparing keys (and values).
IdentityHashMap 使用==替代了equals方法来比较对象是否相同。这一点和 HashMap完全不同。
示例程序
IdentityHashMap map = new IdentityHashMap<>();
map.put(String.valueOf(1), "我是谁");
map.put(String.valueOf(1), "我在哪");
System.out.println(map);
输出:
{1=我是谁, 1=我在哪}
String.valueOf(1) 每次返回的都是新的 String 对象,所以两次put的是不同的对象,所以我们可以看到两个key “相同”的值。
从下面的图可以看出两个“1”是不同的对象。
源码分析
数据结构
从上面的图可以看到,IdentityHashMap是通过table保存kv的,但是它和HashMap,ThreadLocal都不同。它使用的是kv相邻的保存方式,并且有冲突的时候保存到下一个空闲的kv对中。因为是这样的结构,所以 table 的长度必须保持是偶数。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|
k | v |
put方法
public V put(K key, V value) {
// null 会返回一个默认的空对象
final Object k = maskNull(key);
retryAfterResize: for (;;) {
final Object[] tab = table;
final int len = tab.length;
int i = hash(k, len);
// 当 i 已经有值,则跳到下一个key的位置,继续判断。