【面试】Map三件套 - HashMap

2019-10-13  本文已影响0人  最后一页_9b2c

最近在面试中发现,Java集合部分特别喜欢问的三种Map:

    HashMap,Hashtable,ConcurrentHashMap

那么,下面就以我的理解,讲讲上面三个Map集合,如果有什么不对尽请指正,谢谢

HashMap

性质:

    扩容指数:0.75,初始容量16,每次达到容量的0.75,也就是四分之三的时候触发函数  rehash()  进行扩容,允许null键null值,线程不安全。

    特别要注意的是,HashMap为什么不支持线程安全

        假如此时HashMap的容量为16,共有12个元素,那么可以知道,我们已经达到扩容的指数,下一次即将扩容,此时:

        如果多个线程同时对HashMap进行put元素,那么同时会进入  rehash()  函数内部,那么此时就会出现HashMap成环的情况。

    为什么会出现成环的情况?源码如下:

        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;

                if (rehash) {

                    e.hash = null == e.key ? 0 : hash(e.key);

                }

                int i = indexFor(e.hash, newCapacity);

                e.next = newTable[i];

                newTable[i] = e;

                e = next;

            }

        }

    }

先解释一下该函数的过程:

首先获取新表的长度,之后遍历新表的每一个entry,然后每个ertry中的链表,以反转的形式,形成rehash之后的链表。(HashMap存储的数据结构Node<K,V>就是继承于Entry)

    /**     * Basic hash bin node, used for most entries.  (See below for     * TreeNode subclass, and in LinkedHashMap for its Entry subclass.)     */   

static class Node<K,V> implements Map.Entry<K,V> {

        //.......

}

并发问题:

若当前线程此时获得ertry节点,但是被线程中断无法继续执行,此时线程二进入transfer函数,并把函数顺利执行,此时新表中的某个位置有了节点,之后线程一获得执行权继续执行,因为并发transfer。

所以两者都是扩容的同一个链表,当线程一执行到e.next = new table[i] 的时候,由于线程二之前数据迁移的原因导致此时new table[i] 上就有ertry存在,所以线程一执行的时候,会将next节点,设置为自己,导致自己互相使用next引用对方,因此产生链表,导致死循环。

简单来说,就是当第一个线程把扩容完成之时,如果第二个线程正在扩容途中,那么会将next节点设置为自己本身,导致链表成环了。

总结

HashMap在jdk1.7的时候,使用数组+链表,链表用处是来解决Hash冲突的,在插入链表的时候,采用了头插法,但是头插会引起多线程并发问题。

HashMap在jdk1.8的时候,使用了数组+链表/红黑树,链表长度为8的时候,转化为红黑树,长度为6的时候,转回红黑树,8的意思是此时红黑树的使用和查询效率优于链表。

为什么6的时候转回,而不是8?放置频繁转化

jdk1.8的时候HashMap使用尾插法,不会出现并发成环的情况,但是并发会出现数据丢失的情况。

上一篇下一篇

猜你喜欢

热点阅读