HashMap的几个要点:
1、数据存储的底层数据结构
2、扩容机制 与rehash
3、同步问题(HashMap HashTable ConcurrentHashMap的区别与理解)
一、数据结构与存储
1、HashMap底层是通过数组+链表的数据结构实现的。
2、整体来看hashMap中所有数据都存于一个数组table中,数组中的每个元素又是一个链表(hash值相同的元素会被放到一个链表中),链表中的每个节点存入的是每次put进去的元素(put进去的元素会被包装成Node节点)。
3、在put元素obj时,会根据元素obj的key的hash值,计算其应该被放在数组哪个位置,即数组的下标i(计算方法,i=hash值%数组的长度,算出的余数就是下标i)
3.1、计算出下标i后,看数组中table[i]是为空,若为空,则直接让将obj包装成一个Node节点,赋值给table[i](table[i]=new Node(obj) 代码仅作示意);不为空则,则依次比较table[i]这个链表每个元素的val,若相等,则覆盖。若都不相等,则放到链表的最后。
4、为了解决极端情况的hash冲突,jdk1.8引入了红黑树数据结构。当table[i]中的链表节点数大于8时,会将链表转成红黑树进行存储。当节点数小于6时,则会转成链表储存。
扩展阅读 https://blog.csdn.net/Dr_Joker/article/details/57101211
二、扩容机制 与rehash
扩容机制。hashMap中有全负载因子为0.75。当HashMap的size大于等于 table.length*0.75时,会进行扩容。将重新创建一个newTable来进行存储。newTable的length为table.length*2; 扩容后需要进行rehash。
rehash
1、新建一个hash桶newTable,size是原hash桶oldTable的两倍。
2、遍历oldTable中的每个元素,重新计算其hash值,并将其放入到newTable中
2、1 遍历oldTable中的每个元素,(这里的每个元素是链表)
2、1、1 将链表中的头节点取出,暂存于e中,并将该链表在oldTable中的位置置空。
2、1、2 取出e.next元素,暂存于next中
2、1、3 对e进行计算,算出其在newTable中的位置的下标。
2、1、4 计算出下标后,根据下标,将e元素放入到newTable中(newTable的每个元素是链表,是将e放入到链表的头节点)
2、1、4 将next赋值于e,进行while循环。
扩展阅读 https://blog.csdn.net/longwoniu/article/details/48781777
三、同步问题
HashMap是线程不安全的。解决线程不安全的问题有三:
1、HashTable
2、Collections. synchronizedMap(Map)
3、ConcurrentHashMap
1和2都是有Map的每个方法上加上了synchronized关键字,以此来保证线程安全
3 创新点在于对Map进行分段加锁,分成16段分别加锁,所以支持16个线程同时进行写操作。在读的时候不受限制。