1、HashMap是什么?
- HashMap继承自Map接口,是键值对存储的一种集合。
- JDK1.7之前底层使用数组+链表实现;JDK1.8之后当链表长度大于默认值8的时候,链表转换为红黑树
- 一般的put()方法就是将key-value添加到HashMap当中,通过key的hashCode经过扰动函数后得到hash值就是在数组中的位置,之后判断数组中是否已经存在了当前的value,存在则覆盖,不存在通过拉链法解决
Map<Object,Obkect> hashmap = new HashMap<>();
hashmap.put(key,value);
2、put()方法具体经历了什么?
-
HashMap首先判断table是否为null或长度为0,是的话则进行扩容,然后根据key计算hashCode(),得到存储key-value对的位置索引。
-
判断索引处是否为空,若为空,则直接插入到对应的数组中,
-
否则,判断key是否已存在,如果已存在则直接覆盖
-
否则,判断里面是链表还是红黑树,
若是红黑树,则插入到红黑树;
若是链表,则遍历链表插入,java8是将元素插入到链表尾部,
-
插入后,判断链表长度,若长度大于等于8,则将链表转为红黑树,
-
最后判断数组的size()是否大于等于门限值,是的话则进行扩容。
3、查看源码
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
//tab: 引用当前hashMap的散列表
//p: 表示当前散列表的元素
//n: 表示散列表数组的长度
//i: 表示路由寻址结果
Node<K,V>[] tab; Node<K,V> p; int n, i;
// table未初始化或者长度为0,进行扩容(采用了延时初始化,第一次put才会初始化散列表。)
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
// 寻址找到的桶位为null
//(n - 1) & hash 确定元素存放在哪个桶中,桶为空,新生成结点放入桶中(此时,这个结点是放在数组中)
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
// 寻址找到的桶位已经存在元素
else {
Node<K,V> e; K k;
// 比较桶中第一个元素(数组中的结点)的hash值相等,key相等,也就是判断是否是重复的值。
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
// 完全一致
//e:找到的一个与当前要插入的元素一直的元素
e = p;
// hash值不相等,即key不相等;且为红黑树结点
else if (p instanceof TreeNode)
// 放入树中
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
// 为链表结点
else {
// 在链表最末插入结点
for (int binCount = 0; ; ++binCount) {
// 到达链表的尾部
if ((e = p.next) == null) {
// 在尾部插入新结点
p.next = newNode(hash, key, value, null);
// 结点数量达到阈值,转化为红黑树
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
// 判断链表中结点的key值与插入的元素的key值是否相等
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
// 相等,跳出循环
break;
// 用于遍历桶中的链表,与前面的e = p.next组合,可以遍历链表
p = e;
}
}
// 表示在桶中找到key值、hash值与插入元素相等的结点
if (e != null) {
// 记录e的value
V oldValue = e.value;
// onlyIfAbsent为false或者旧值为null
if (!onlyIfAbsent || oldValue == null)
//替换
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
// 结构性修改
++modCount;
// 实际大小大于阈值则扩容
if (++size > threshold)
resize();
// 插入后回调
afterNodeInsertion(evict);
return null;
}