HashMap
数据结构特点
其类定义如下
1 | public class HashMap<K,V> extends AbstractMap<K,V> |
继承了AbstractMap,实现了Map、Cloneable、Serializable,注意并没有实现Collection接口。
HashMap允许K/V值为null,非线程安全,不保证有序。
1.7版本与1.8版本区别
JDK1.7的hashmp数据结构为数组+链表,而JDK1.8的数据结构为数组+链表+红黑树,加入红黑树是为了解决发生hash碰撞后链表过长从而导致索引效率问题,主要利用了红黑树快速增删改查的特点,时间复杂度从O(n)降为O(logn)。
链表转红黑树条件:链表长度>8时,将链表转化为红黑树。
- 无冲突:存放在数组
- 有冲突但链表长度<8:存放在单链表
- 有冲突但链表长度>8:转化为红黑树并存放
构造方法
4个构造方法,可指定初始容量、负载因子以及输入一个map
1 | public HashMap(int initialCapacity, float loadFactor) { |
初始化参数
1 | //默认初始桶大小为16,必须是2的幂次 |
Node定义
1 | //hashmap对链表节点的定义 |
TreeNode定义
继承了LinkedHashMap的Entry类,有左右指针和前后指针以及颜色属性。
1 | static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> { |
put方法
put方法主体函数
1 | public V put(K key, V value) { |
hash函数
无论1.7还是1.8版本都对hashcode进行了扰动处理,这样做是为了加大哈希码的随机性使分布更均匀,减少hash冲突
1 | //将key转化为hashcode,调用了一次hashCode + 1次位运算 + 1次异或运算 |
计算table存储下标
在1.8中没有专门的函数计算entry在桶中的存储位置,只是通过一句代码来完成,即
1 | //h即hashcode,length即table的长度 |
putVal操作
1 | final V putVal(int hash, K key, V value, boolean onlyIfAbsent, |
将链表替换为红黑树
当一个桶中的元素个数超过TREEIFY—THRESHOLD(默认是8),就会将链表转化为红黑树。
1 | //该方法将链表节点替换为红黑树节点 |
初始化红黑树
treeifyBin方法只将链表节点替换成了红黑树节点,并没有建立起红黑树的结构,接下来需要调用treeify方法构建红黑树
1 |
|
插入新节点时保证红黑树特性
这里会根据红黑树特性调整结构
1 | static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root, |
扩容操作
以下两种场景需要扩容:1.初始化哈希表 2.当前数组容量过小进行扩容
1 |
|
get操作
1 | public V get(Object key) { |