概览
HashMap的继承关系
java.lang.Object
↳ java.util.AbstractMap<K, V>
↳ java.util.HashMap<K, V>
public class HashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
}
HashMap实现了Map接口,是一个存储键值对映射的集合,线程不安全,无序,键和值可以为null。
底层数据结构
HashMap的底层维护着一个Entry数组,即所谓的hash表,表中元素是Entry链表的第一个节点,链表中存储的是hash冲突的元素。
Java8后Entry改名为Node,并且当链表的容量达到一定值的时候转换成红黑树
源码解析
HashMap的静态属性值
//hash表默认容量:16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
//最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;
//装填因子,默认0.75
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//将冲突链表转为红黑树的阈值
static final int TREEIFY_THRESHOLD = 8;
//由红黑树转链表时,数节点的阈值, 小于这个阈值才能转为链表
static final int UNTREEIFY_THRESHOLD = 6;
//能将链表转为红黑树的最小hash表容量。应该至少4倍于 TREEIFY_THRESHOLD
//即只有当 冲突链表节点数大于等于8 且 hash表容量达到64时才将链表转为红黑树
static final int MIN_TREEIFY_CAPACITY = 64;
put()方法
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
这里直接调用了 hash()
方法计算 key
的hash值 并调用 putVal()
方法
计算hash
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
当 key
为 null
,其hash值为0.
h = key.hashCode()
计算出 key
的hash值,h >>> 16
是因为计算出的hash值是32位的,将h无符号右移16位,再与原值异或运算。得到的新hash值的高16位较h的原值不变,原值的低16位和高16位进行了异或运算。
这么做的主要目的在于,当hash数组较小时,hash值的所有bit位都参与了运算,防止hash冲突太大。
putVal()详解
//hash: 当前key的hash值, onlyIfAbsent:是否覆盖重复值,hashMap 里面是false,evict:没用。。
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,