HashMap
- 利用hash函数快速定位元素位置,以达到O1的读写速度
- hash->数组->取余
- hash:调用key的hashCode()方法
- hash冲突
- 高位参与运算,HashMap容量是2的倍数,因此只有低位才参与运算,高位不参与运算,可能造成hash冲突几率增加。
- 拉链法:链表+红黑树
- 线性探索法
- 数组大小
- 容量都是:2的幂
- 初始默认容量:16
- 链表转红黑树:8,容量64
- 红黑树转链表:6
- 扩容:只有扩容无缩容
- 负载因子:默认0.75
- 两倍扩容,迁移元素少,只需要迁移一半元素
- 单节点,e.hash & (newCap - 1)
- 链表,拆链表:
- e.hash & oldCap==0,hash新增那个bit是0还是1,
- 0则存到低位链表,1则存到高位链表,拆分为两个链表。
- 尾插法,无循环链表问题。但是依旧线程不安全。
- JDK1.7是头插法,倒序插入,多线程下可能形成循环链表
- 红黑树:拆树,如果拆分后的树小于等于6,则转为链表
结构
机制
算法优化
寻找大于某个数的最小的2n
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
hash优化
int hash = (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16)
int index = hash&(2n-1) = hash%2n
HashMap容量是2的倍数,因此只有低位才参与运算,高位不参与运算,可能造成hash冲突几率增加。
resize扩容时
e.hash & oldCap==0 判断是高位还是低位,直接存入对应链表
链表转红黑树
- put后 链表长度>=8 并且 表的长度>=64
红黑树转链表
- 扩容-拆树,如果拆分后的树小于等于6,则转为链表
- 在红黑树的root节点为空 或者root的右节点、root的左节点、root左节点的左节点为空时
扩容
- 默认数组长度16,负载因子0.75
- 每次扩容两倍,初始化新table
- 迁移数据
- 单节点,节点在新table中的下标:e.hash & (newCap - 1)
- 链表:拆链表,通过e.hash & oldCap==0,hash新增那个bit是0还是1,0则存到低位链表,1则存到高位链表,拆分为两个链表。
移动只需将低位和高位链表的头插入到对应的槽位中即可(j,j + oldCap),无循环链表问题,但是依旧不是线程安全的 - 红黑树:拆树,如果拆分后的树小于等于6,则转为链表
- 扩容时机
- 第一次put,初始化table
- put后 链表长度>=9 并且 表的长度<64
- put后 size 大于 数组长度*负载因子
流程
get
- 大致流程为:
- hash(key)%tab.size,取得元素下标,
- 判断key是否相等,
- 相等返回
- 不等
- 链表,循环元素,key相等返回
- 红黑树,按hash值排序的,若hash和key均相等,则返回
put
remove待补充
红黑树转链
源码
/* ---------------- 类属性 -------------- */
// 默认容量16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
// 最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;
// 默认负载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;
// 链表转红黑树的阈值:9
static final int TREEIFY_THRESHOLD = 8;
// 红黑树转链表的阈值:6
static final int UNTREEIFY_THRESHOLD = 6;
// 转红黑树时,table的最小长度
static final int MIN_TREEIFY_CAPACITY = 64;
/* ---------------- 静态类 -------------- */
//
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
// 省略
}
// 红黑树节点
// LinkedHashMap.Node
// static class Entry<K, V> extends Node<K, V> {
// LinkedHashMap.Entry<K, V> before;
// LinkedHashMap.Entry<K, V> after;
// 省略
// }
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
TreeNode<K,V> parent; // red-black tree links
TreeNode<K,V> left;
TreeNode<K,V> right;
TreeNode<K,V> prev; // needed to unlink next upon deletion
boolean red;
//省略
}
/* ---------------- 实例属性 -------------- */
// HashMap的用Node数组存放数据,结构为Node数组+链表+红黑树的结构
transient Node<K,V>[] table;
// 缓存
transient Set<Map.Entry<K,V>> entrySet;
// HashMap中存放Node的数量
transient int size;
// 负载因子
final float loadFactor;
// 下一次触发resize的值 = capacity(table size) * load factor
int threshold;
// 结构修改的次数 :HashMap中的映射数量或以其他方式修改其内部结构(例如,重新散列),用于实现快速失败机制
transient int modCount;