JDk1.8HashMap的源码分析
HashMap用数组存放数据(总结)
- 调用键的hashCode()获得键的哈希值
- 用哈希值,计算一个下标值 i
- 新建Entry对象来封装键和值
- Entry对象放在 i 位置
- 空位置,直接放入
- 有数据,依次用equals()比较是否相等
- 找到相等的,覆盖值
- 没有相等的,链表连接在一起
- 负载率、加载因子到0.75
- 新建翻倍容量的新数组
- 所有数据,重新哈希,放入新数组
- jdk1.8
- 链表长度到8,转成红黑树
- 红黑树数据量减少到6,转回成链表
hashCode()
Object的方法
Object中默认实现是使用内存地址,作为哈希值
如果对象作为键,放入HashMap,应该重写hashCode(),使用属性数据,来计算哈希值
HashMap中根据hash值求得index的
// 先用key求得hash值
static final int hash (Object key){
// h = key.hashCode() 为第一步 取hashCode值
// h ^ (h >>> 16) 为第二步 高位参与运算
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
JDK1.8HashMap的put方法源码如下
/**
* (1).判断键值对数组table[i]是否为空或为null,否则执行resize()进行扩容;
* (2).根据键值key计算hash值得到插入的数组索引i,如果table[i]==null,直接新建节点添加,转向(6),如果table[i]不为空, 转向(3);
* (3).判断 table[i]的首个元素是否和key一样,如果相同直接覆盖value,否则转向(4),这里的相同指的是hashCode以及equals;
* (4).判断table[i] 是否为treeNode,即table[i] 是否是红黑树,如果是红黑树,则直接在树中插入键值对,否则转向(5);
* (5).遍历table[i],判断链表长度是否大于8,大于8的话把链表转换为红黑树,在红黑树中执行插入操作,
* 否则进行链表的插入操作; 遍历过程中若发现key已经存在直接覆盖value即可;
* (6).插入成功后,判断实际存在的键值对数量size是否超多了最大容量threshold,如果超过,进行扩容
*/
public V put (K key, V value){
// 对key的hashCode()做hash
return putVal(hash(key), key, value, false, true);
}
final V putVal ( int hash, K key, V value,boolean onlyIfAbsent,
boolean evict){
Node<K, V>[] tab;
Node<K, V> p;
int n, i;
// 步骤(1):tab为空则创建
if ((tab = table) == null || (n = tab.length) == 0) {
n = (tab = resize()).length;
}
// 步骤(2):计算index,并对null做处理
if ((p = tab[i = (n - 1) & hash]) == null) {
tab[i] = newNode(hash, key, value, null);
} else {
Node<K, V> e;
K k;
// 步骤(3):节点key存在,直接覆盖value
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k)))) {
e = p;
// 步骤(4):判断该链为红黑树
} else if (p instanceof HashMap.TreeNode) {
e = ((TreeNode<K, V>) p).putTreeVal(this, tab, hash, key, value);
// 步骤(5):该链为链表
} else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
//链表长度大于8转换为红黑树进行处理
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
{
treeifyBin(tab, hash);
}
break;
}
// key已经存在直接覆盖value
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
break;
}
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null) {
e.value = value;
}
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
// 步骤(6):超过最大容量 就扩容
if (++size > threshold) {
resize();
}
afterNodeInsertion(evict);
return null;
}
美团的Java 8系列之重新认识HashMap
参考:https://tech.meituan.com/2016/06/24/java-hashmap.html