HashMap
1. 底层数据结构?
JDK1.7之前为链表+数组,JDK1.8后为链表+数组+红黑树
数组是为了存储数据,链表为了解决hash冲突,红黑树为了方便查询
链表长度到达8后hashmap,只有当链表长度>8且数组长度>=64时链表才会转换为红黑树。
2. hash算法
hashmap的put和get
1.先根据key的值计算hash值
(key == null) ? 0 : (h = key.hashcode() ^ (h >>> 16)) 向右无符号移动16位
2. 根据hash值计算数组下标
(length - 1) & hash
目的是让数组下标更加分散
3. HashMap扩容机制
HashMap上的元素个数达到临界值(容量 * 负载因子)后会触发扩容,会重新rehash所有元素后放入扩容容器(新建的原数组两倍大)中
4. 为什么负载因子为0.75呢
提供了一个良好的时间与空间成本的权衡(泊松分布)
较大时会在扩容前发生更多的hash冲突,较小时会占用较多空间
5. JDK1.7 与 JDK1.8的改变
1. 数据结构
增加了红黑树
2. 链表的插入方式由头插法改为尾插法
JDK1.7之前头插法会使链表反转,多线程条件下会形成环
3. 扩容rehash
JDK1.7之前扩容需要重新计算hash找位置,JDK1.8后不需要重新计算,新的位置不变或移动2的幂
4. 扩容时机
JDK1.7前先判断是否需要扩容,再插入
6. HashMap HashTable
两者都基于哈希表实现
区别:
1. 继承的类不同,但都实现了map接口
hashmap继承abstractMap类,hashtable实现了
2. hashmap线程不安全,hashtable安全
3. HashTable不可以有Null值,而HashMap可以有
7. HashSet
底层为hashmap,hashset相当于其中的key
不能有重复数字,无序
8. ConcurrentHashMap
是安全的
JDK1.7前 是双数组结构 Segment + HashEntry
Segment extends ReentranLock 为可重入锁
HashEntry 被voliate 修饰,具有可见性
put方法先由key选择出segment ,放入后加锁在放入HashEntry中
JDK1.8前
数组 + 链表 + 红黑树
锁结构变为了 CAS + synchronized
锁变化的原因
1.ynchronized进行了优化,有多种锁状态,无锁-> 偏向锁 -> 轻量级锁 -> 重量级锁
2.减小内存消耗
9. linkedHashMap TreeMap
两者均为有序的map
linkedhashMap 底层为hashMap的数据结构和双向链表
treeMap 底层数据结构为红黑树,排序是根据比较器进行排序,无比较器则根据key的自然排序