Hashmap、HashTable、ConcurrentHashmap三者区别(未完待续)

在对三者进行比较之前,需要先掌握三者的实现原理

1.实现原理

1.1 Hashmap

Hashmap的底层实现原理要分成jdk1.8之前和jdk1.8之后两种情况叙述。

(1)在jdk1.8以前,Hashmap底层是数组和链表结合在一起使用,也就是链表散列

        HashMap 通过 key 的 hashCode 经过扰动函数处理过后得到 hash 值,然后通过 (n - 1) & hash 判断当前元素 存放的位置(这⾥的 n 指的是数组的⻓度),如果当前位置存在元素的话,就判断该元素与要存⼊的元素的 hash 值以及 key 是否相同(先用hash值比较,相同的话用key对应的equals进行比较),如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。
        注:拉链法就是每一个数组元素都是一个链表,所有hash值相同但是key值不同的元素都用一个链表串联起来,再把这条链表存入到数组中对应的位置(如下图所示)。

 (2)在jdk1.8以后,Hashmap底层是数组和链表\红黑树

在jdk1.8以后解决哈希冲突的方法变了,jdk1.8以前的存储方式可能出现的一种问题是,如果发生冲突的键很多,假设最差情况下,所有的键都通过一个链表存储在数组的同一个元素中,那么此时查找所需要的时间复杂度就是O(n),所以在jdk1.8以后,如果链表长度大于8且节点数组长度大于64的时候,就把链表下所有的节点转为红黑树。红黑树的查找时间复杂度一直是O(logn),所以也就解决了上面这种问题。

需要注意的是:将链表转换成红⿊树前会判断,如果当前数组的⻓度⼩于 64,那么会选择先进⾏数组扩容,⽽不是转换为红⿊树。

(3)Hashmap的扩容原理:

当HashMap决定扩容时,会调用HashMap类中的resize(int newCapacity)方法,参数是新的table长度。在JDK1.7和JDK1.8的扩容机制有很大不同。

JDK1.7下的resize()方法是这样的:  

void resize(int newCapacity) {
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }
 
        Entry[] newTable = new Entry[newCapacity];
        transfer(newTable, initHashSeedAsNeeded(newCapacity));
        table = newTable;
        threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
    }


代码中可以看到,如果原有table长度已经达到了上限,就不再扩容了。

如果还未达到上限,则用新容量创建一个新的table,并调用transfer方法把原table的Node放到新的table中:   

/**
     * Transfers all entries from current table to newTable.
     */
    void transfer(Entry[] newTable, boolean rehash) {
        int newCapacity = newTable.length;
        for (Entry<K,V> e : table) {
            while(null != e) {
                Entry<K,V> next = e.next;              //注释1
                if (rehash) {
                    e.hash = null == e.key ? 0 : hash(e.key);
                }
                int i = indexFor(e.hash, newCapacity); //注释2
                e.next = newTable[i];                  //注释3
                newTable[i] = e;                       //注释4
                e = next;                              //注释5
            }
        }
    }


transfer方法的作用是把原table的Node放到新的table中,使用的是头插法,也就是说,新table中链表的顺序和旧列表中是相反的,在HashMap线程不安全的情况下,这种头插法可能会导致环状节点。
 

参考文献:

https://blog.csdn.net/lkforce/article/details/89521318 Hashmap实现原理及扩容机制详解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值