1、HashMap
1.1 HashMap结构
JDK1.7和JDK1.8的结构不同
在JDK1.7中,key和value构成Entry节点,所有的Entry节点构建一个数组,数组的下标是key的HashCode取模。另外,对于hashCode一样但是key值不一样的元素,会放在同一个Entry节点中,并且他们在这个Entry节点内部会构成一个单向链表。
在JDK1.8中,key和value组成Node节点,所有的Node节点构建一个数组,数组的下标是key的HashCode取模。这个跟JDK1.7一样。但是在Node节点内部就不一样了,如果Node节点内的元素小于等于8个,还是跟JDK1.7一样是单向链表结构,如果大于8个元素,就会自动变成红黑树结构。红黑树结构比链表结构的时间复杂度要低,直接提升了性能。
1.2 HashMap细节
HashMap允许有一条空数据。
HashMap中Node数组的初始长度是16,并且后面自动扩容的长度也是2的幂数。
HashMap中元素放在数组的哪个位置(下标)是计算出来的:
1)先把元素的key取hashcode,得到一个值;
2)然后把这个值用hashMap自己的HashCode()方法再取一遍Hash值,目的是为了让这个值的0和1尽量分散;
3)最后把这个值跟(数组长度-1,也就是11111111111)做&运算,得到一个小于数组长度的值。这个值就是放的位置。
1.3 HashMap扩容机制
HashMap必须同时满足两个条件才会扩容:
1)存放新值的时候当前已有元素的个数必须大于等于阈值(数组长度*负载因子0.75)
2)存放新值的时候当前存放数据发生hash碰撞(当前key计算的hash值换算出来的数组下标位置已经存在值)
1.4 HashMap Resize
Resize()方法用来给hashMap扩容,扩容的时机在上面讲了,下面讲的是扩容的方法:
1)首先,计算需要扩容的新NODE数组的长度,是扩容2倍,还是几倍。
2)然后,重新申明一个新的NODE数组,长度是计算好的值 ;
3)所有元素重新计算hashcode,复制到新的位置。不仅包括NODE数组的位置移位,还包括单向链表和红黑树节点的数据移位。
1.5 HashMap死锁原因
HashMap死锁,体现了HashMap的线程不安全性,是无法避免的,原因是:
如果两个线程都刚好同时碰到了HashMap需要扩容的时机,扩容时HashMap需要做rehash,重新生成新数据,那么两个线程分别往新的Node数组里放元素的时候,由于多线程并发执行的原因,可能出现链表中元素的next节点互相是对方节点的情况,导致死锁。
1.6 ConCurrentHashMap
JDK1.7与JDK1.8实现方式完全不同,一个是分段锁,一个是对node加锁。
1)JDK1.7实现:
是线程安全的,区别于hashtable,concurrenthashmap是将数据分成多个段,每个段分别加锁,也就是段锁。而hashtable是对数据整个加锁,两者效率明显不一样,对segment加锁的话,其它segment的数据还能继续操作。
Concurrenthashmap的put操作、get操作、remove操作都下放到每一个segment里面去操作的。我们通过对插入数据的key的hashcode再进行一次hash,得到segment的值,从而确定每一个key所在的segment。
至于Count计数方法,则是把每个segment里面的count相关,得到全部值的和。
2)JDK1.8实现
相对于JDK1.7进行了两点改变,分别是:
——取消了segmant,而是采用transient volatile HashEntry<K,V> table[]保存数据,对table数组的每个Node加锁,粒度更小,即实现了线程安全,又提高了性能;
——同JDK1.8Hashmap的改变,Node内部结构支持单向链表+红黑树两种存储结构,提高查询效率。
2、 LinkedhashMap
是双向链表的hashmap,也是有序的hashmap。HashMap存进去的数据取出来是没顺序的,但是linkedHashMap是有顺序的。
LinkedHashMap的顺序可以有两种,一种是按插入顺序排序的,一种是按照操作顺序排序的。
因此,如果需要实现LRU(最近最少使用),只需要使用LinkedHashMap的按操作顺序排序方式,最近使用的在LinkedHashMap的链表头位置,最久没使用的在链表尾位置。
2.1 ConcurrentLinkedHashMap
是LinkedHashMap的线程安全变种类。
3、TreeMap
Entry节点组成的红黑树,红黑树是一个自平衡的排序二叉树,节点值比左边所有子节点的值都大,比右边所有子节点的值都小。
4、HashMap、TreeMap和LinkedHashMap使用场景
首先,这三种数据结构的主要特点如下:
HashMap:增删改查时间复杂度O(1),统计最大最小值时间复杂度O(N)
TreeMap:增删改查统计的时间复杂度O(logN)
LinkedHashMap:时间复杂度同HashMap,但插入顺序和读出顺序一致
所以,使用场景分别如下:
HashMap:适合不要求顺序的增删改查操作
TreeMap:适合基于大小排序的统计操作
LinkedHashMap:适合要求顺序的增删改查操作
5、HashTable
是线程安全的,功能类似于HashMaP,出现的比HashMap要早,现在已经不用了。现在线程安全的都是concurrentHashMap,比Hashtable效率高多了