HashMap 原理浅析
一、HashMap结构图
二、HashMap中心思想
无论是put(增加/修改)、get(查询)、还是remove(删除)操作,其操作的核心思想就是先找到要操作的那个节点。
那如何找寻呢?
1.由图可知,先找到其所在table的哪个索引下的元素(先把key处理得出hash值,然后hash&(table.length)也就是取模得到 其所在table上元素的位置)
2.然后这里就会在这table元素下所挂的链表里遍历寻找,
a.源码中的处理都是会判断当前table索引下有没有元素(也就是有没有链表的头节点,同时也是标识是否存在链表)
b.然后头节点判断,若有则会进行链表的遍历(当前的头节点也不是),
1)判断当前头节点是否是树节点? 若是则交给红黑树的遍历逻辑里去寻找
2)上面已经判断是否是树节点,若不是则在链表里遍历寻找
看完上面如何去寻找要操作的节点的思路,然后去看put()、get()、remove()操作的源码就会很清晰
三、这里就介绍一下put()的源码详细解析示例
// An highlighted block
HashMap put()方法总结:
一.先看HashMap中 数组表是不是为null或者数组的长度是不是为0,如果是则进resize()方法进行扩容;
//a.此判断在第一次put()方法的时候会进 或者
//b.new HashMap时带HashMap对象参数 HashMap(Map<? extends K, ? extends V> m)时会进
二.
1.if再判断数组表格里的数据是否为null(也就是头节点),并且记录下来,如果为null,那么就直接new 一个Node对象并放进数组中
2.else
if比较头节点(数组里的节点)的key相不相同 (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))
相同就记录下来
else if 头节点是否属于树节点? 是就放到树节点里面去处理putTreeVal()
else (那肯定不是树节点)
a.for 那就循环找节点的next为null的节点或者key值相同的节点,
1.next为null则会放进去,同时把下一个节点e记录下来
2.若相同则break
b.判断下一个节点e是否为null
(此时的e,若找到相同key的节点,则为key值相同原先的节点,若没找到则为null)
if e!=null则说明找到 onlyIfAbsent为false 或者 oldValue == null则覆盖旧值,最后返回旧值
三.
1.记录修改次数 ++modCount;
2.if (++size > threshold) resize(); size加1 并且判断是否超过了阈值threshold,若是则扩容 到原来数组长度length的2倍
3.afterNodeInsertion(evict);留一个空方法 其他的Map类会有重写扩展,比如LinkedHashMap
4.返回null
附上源码:
put()源码:
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
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;
}
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;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
上面是put()方法的详解,其他操作都差不多,按照中心思想去读源码,就能一目了然
注:
更多源码的详细解析,我会写在CSDN里,后续我会慢慢补上所有操作源码的解析
CSDN链接: https://blog.csdn.net/qq_42307562 HashMap 模块
此文件地址:https://www.processon.com/diagraming/5f01965fe401fd3908b214ed