HashMap
HashMap继承自AbstractMap,实现了Map:
HashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>
流程:
-
计算
hashcode
-
高位无符号右移16位以参与异或运算(大多数length一般都小于2^16即小于65536)
-
取模:(length-1)& hash(就是低位全1,保留了hashcode的零散性质)
-
扩容:数组的长度或扩大到原来的2,4,8倍以上,这样做的原因是,取余的时候很好算
原hash新增位是1,新索引位置就是加一个旧的容量值,新增位是0,则不变
1.1 几个重要的成员变量:
transient int size; //
transient int modCount; // HashMap被改变的次数
int threshold; // 门限值
final float loadFactor; // 装载因子
static class Node<K, V> implements Entry<K, V>{}// 静态内部类,实现链表
transient HashMap.Node<K, V>[] table; // 实现数组-链表
transient Set<Entry<K, V>> entrySet; //
initialCapacity
默认为16,loadFactory
默认为0.75,容量等于16*0.75=12
1.2 构造函数:
HashMap
提供了四个构造函数,用于指定门限值和装载因子:
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0) {
throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);
} else {
if (initialCapacity > 1073741824) {
initialCapacity = 1073741824;
}
if (loadFactor > 0.0F && !Float.isNaN(loadFactor)) {
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
} else {
throw new IllegalArgumentException("Illegal load factor: " + loadFactor);
}
}
}
1.3 Put方法
索引冲突的几种种情况:
-
key值相同:进行值覆盖
-
key值不同,hash相同:链表或者树
-
key值不同,hash不同,取模后相同(索引位置相同):链表或者树
public V put(K key, V value) {
return this.putVal(hash(key), key, value, false, true);
}
// hash 计算hash值
static final int hash(Object key) {
int h;
// 支持 key = null 的形式,
// hashcode ^ (h >>> 16)
// 高位参与运算,大多数length一般都小于2^16即小于65536
return key == null ? 0 : (h = key.hashCode()) ^ h >>> 16;
}
// putval
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict){
HashMap.Node[] tab; // Node类实现了一个链表数组
// 数组的长度总是 2的n次方
int n; // n是tab的长度,第一次put或者中途扩容的时候会重新设置
if ((tab = this.table) == null || (n = tab.length) == 0) {
n = (tab = this.resize()).length;
}
Object p;
int i;
// 根据hash计算索引位置,若空则插入一个node
if ((p = tab[i = (n - 1) & hash]) == null) {
tab[i] = this.newNode(hash, key, value, (HashMap.Node)null);
} else { //说明待插入位置存在元素
Object e;
Object k;
if(((HashMap.Node)p).hash == hash &&
((k = ((HashMap.Node)p).key) == key ||
key != null && key.equals(k))){
e = p;
} else if(p instanceof HashMap.TreeNode){ // p是红黑树
e = ((HashMap.TreeNode)p).putTreeVal(this, tab, hash, key, value);
} else {
int binCount = 0;
while(true) {
// 遍历这个节点,如果 binCount > 7 转为红黑树,
// code
break;
// 否则接在列表后面
// code
break;
++binCount;
}
}
if (e != null) {
// 修改原值~
}
}
}
Node内部类:像是一个链表的形式
static class Node<K, V> implements Entry<K, V> {
final int hash;
final K key;
V value;
Node<K, V> next;
}
1.4 get方法:
public V get(Object key) {
HashMap.Node e;
// 是根据输入节点的 hash 值和 key 值利用getNode 方法进行查找
return (e = this.getNode(hash(key), key)) == null ? null : e.value;
}
final HashMap.Node<K, V> getNode(int hash, Object key) {
HashMap.Node[] tab;
HashMap.Node first;
int n;
// tab非空,长度>0,索引处非空
if ((tab = this.table) != null && (n = tab.length) > 0 && (first = tab[n - 1 & hash]) != null) {
Object k;
// 检查头节点
if (first.hash == hash && ((k = first.key) == key || key != null && key.equals(k))) {
return first;
}
HashMap.Node e;
if ((e = first.next) != null) {
// 若定位到的节点是 TreeNode 节点,则在树中进行查找
if (first instanceof HashMap.TreeNode) {
return ((HashMap.TreeNode)first).getTreeNode(hash, key);
}
// 否则在链表中进行查找
do {
if (e.hash == hash && ((k = e.key) == key || key != null && key.equals(k))) {
return e;
}
} while((e = e.next) != null);
}
}
return null;
}
1.5 Resize方法
我们在扩容的时候,一般是把长度扩为原来2倍,所以,元素的位置要么是在原位置,要么是在原位置再移动2次幂的位置。(左边为0不移动,左边为1移动)
0000 0000 0000 0000 0000 0000 0000 1111 n = 16
1111 1111 1111 1111 0000 1111 0000 0101 0101 5
1111 1111 1111 1111 0000 1111 0001 0101 0101 5
0000 0000 0000 0000 0000 0000 0001 1111 n = 32
1111 1111 1111 1111 0000 1111 0000 0101 00101 5
1111 1111 1111 1111 0000 1111 0001 0101 10101 5 + 16
因此,我们在扩充HashMap的时候,只需要看看原来的hash值新增的那个bit是1还是0就好了,是0的话索引没变,是1的话索引变成“原索引+oldCap”
final HashMap.Node<K, V>[] resize() {
HashMap.Node<K, V>[] oldTab = this.table; // 把旧的表装好
int oldCap = oldTab == null ? 0 : oldTab.length; // 计算旧表的容量
int oldThr = this.threshold; // 计算旧表门限
int newThr = 0;
int newCap;
if (oldCap > 0) { // 原表非空的情况
if (oldCap >= 1073741824) { // 超出去就不扩了
this.threshold = 2147483647;
return oldTab;
}
if ((newCap = oldCap << 1) < 1073741824 && oldCap >= 16) {
newThr = oldThr << 1;
}
} else if (oldThr > 0) { // 原表是空(其它三个构造函数)
newCap = oldThr; // 容量设定为门限
} else {
newCap = 16; // 空参构造函数创建的
newThr = 12;
}
if (newThr == 0) { // 设定门限
float ft = (float)newCap * this.loadFactor;
newThr = newCap < 1073741824 && ft < 1.07374182E9F ? (int)ft : 2147483647;
}
this.threshold = newThr; // 开始创建新表
HashMap.Node<K, V>[] newTab = new HashMap.Node[newCap];
this.table = newTab;
if (oldTab != null) {
for(int j = 0; j < oldCap; ++j) {
// rehash开始
HashMap.Node e;
if ((e = oldTab[j]) != null) {
// 原表置空
oldTab[j] = null;
if (e.next == null) {
// 原节点是单点,用它的hash值,重新计算索引
newTab[e.hash & newCap - 1] = e;
} else if (e instanceof HashMap.TreeNode) {
// 红黑树rehash
((HashMap.TreeNode)e).split(this, newTab, j, oldCap);
} else {
// 链表rehash
Node<K,V> loHead = null, loTail = null; // 分成两组,索引移位和不移位
Node<K,V> hiHead = null, hiTail = null; //
Node<K,V> next;
do {
next = e.next; // 保存下一个节点
if ((e.hash & oldCap) == 0) { // 扩2倍对应的值是0
if (loTail == null) {
loHead = e;
} else {
loTail.next = e;
}
loTail = e; // loTail = loTail.next
} else {
if (hiTail == null) {
hiHead = e;
} else {
hiTail.next = e;
}
hiTail = e;
}
e = next; // 遍历链表操作
} while(next != null);
if (loTail != null) { // 分别移动到新数组上
loTail.next = null; // 低位直接复制
newTab[j] = loHead;
}
if (hiTail != null) { // 高位 原索引+原容量
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}