IdentityHashMap

释放双眼,带上耳机,听听看~!

在这一篇中我们将介绍一下IdentityHashMap集合的相关知识。看名字我们知道IdentityHashMap集合底层是通过HashMap集合实现的。那么按照我们分析其它集合一样,它也一定是具有某种特殊的特性的,否则Java也不会提供一个单独的IdentityHashMap集合了。我们先不介绍IdentityHashMap集合有哪种特性,我们先看下面的例子,然后在做详细说明。

public class Test {
    @org.junit.Test
    public void test() throws Exception {
        IdentityHashMap identityHashMap = new IdentityHashMap();
        identityHashMap.put(new String("key"), "value");
        identityHashMap.put(new String("key"), "value");
        HashMap hashMap = new HashMap();
        hashMap.put(new String("key"), "value");
        hashMap.put(new String("key"), "value");
        System.out.println(String.format("IdentityHashMap: %s", identityHashMap));
        System.out.println(String.format("HashMap: %s", hashMap));
    }
}
IdentityHashMap: {key=value, key=value}
HashMap: {key=value}

看输出结果我们对HashMap集合的输出结果不会感到意外,但是IdentityHashMap集合的结果却很让人费解。因为按照我们以前对集合的分析,不是说在HashMap中不允许有重复的key存在吗,如果有相同的key则后添加到集合中的元素的value会覆盖前一个元素中的value,这也是我们在分析HashMap那篇文章中总结出的HashMap的特性。那么为什么在IdentityHashMap集合中却可以保存两个相同的key呢?其实答案很简单,这就是IdentityHashMap集合的特性所在。

我们在分析HashMap底层实现时知道,在HashMap集合中首先会判断key的hashCode是否相等,然后在判断key是否相等。如果这两个判断都相等则认为当前存储的两个元素的key是相等的,于是就执行value的覆盖操作。并且上述判断不但会判断==还会用equals方法判断。这也是为什么在将对象类型的数据保存在HashMap集合中必须重写equals方法的根本原因。如果不这样做HashMap集合就会认为这两个元素的key不是相同的。这也就是IdentityHashMap集合与HashMap集合的底层实现不同的根本所在,就是因为在IdentityHashMap集合中只会用==判断两个key是否相等,而不会调用key的equals方法判断。如果两个key==相等则IdentityHashMap集合就会认为这两个key相等,于是执行value的覆盖操作。如果保存在IdentityHashMap集合中的key==不相等,则就会把这两个key所对应的元素都保存在集合中,而不会执行value的覆盖操作。

在上述代码中我们创建了两个String对象,并且我们知道在Java中如果不是基本数据类型采用==比较的话,它们实际比较的是内存地址。因为我们创建了两个String对象,所以上述的比较结果一定为false。所在IdentityHashMap集合就会认为这两个key是不同的key,于是将这两个元素都保存在集合中。而在HashMap集合中除了进行上述比较外,还会调用key的equals方法进行比较。因为我们保存的key是一个String,并且在String这个类中已经重写了equals方法,所以调用该方法实际比较的就是真正的值,也就是key和key做比较。正是因为它们相等,所以HashMap集合就会执行value的覆盖操作。所以在输出的结果中HashMap集合只会保存一个元素。下面我们看一下IdentityHashMap集合的底层源码,来证明我们上述所说的。

public V put(K key, V value) {
    final Object k = maskNull(key);

    retryAfterResize: for (;;) {
        final Object[] tab = table;
        final int len = tab.length;
        int i = hash(k, len);

        for (Object item; (item = tab[i]) != null;
             i = nextKeyIndex(i, len)) {
            if (item == k) { //直接采用==比较而没有调用equals方法比较
                @SuppressWarnings("unchecked")
                    V oldValue = (V) tab[i + 1];
                tab[i + 1] = value;
                return oldValue;
            }
        }

        final int s = size + 1;
        if (s + (s << 1) > len && resize(len))
            continue retryAfterResize;

        modCount++;
        tab[i] = k;
        tab[i + 1] = value;
        size = s;
        return null;
    }
}
0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
今日签到
有新消息 消息中心
有新私信 私信列表
搜索