我是靠谱客的博主 轻松水池,最近开发中收集的这篇文章主要介绍jdk13 HashMap中put方法源码分析,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

import java.util.HashMap.Node;
import java.util.HashMap.TreeNode;
/**
* Associates the specified value with the specified key in this map.
* If the map previously contained a mapping for the key, the old
* value is replaced.
*
* 在这个map中将指定的value与指定的key联系起来,如果map事先已经包含了这个key
* 那么老的value会被取代。
*
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
* @return the previous value associated with {@code key}, or
*
{@code null} if there was no mapping for {@code key}.
*
(A {@code null} return can also indicate that the map
*
previously associated {@code null} with {@code key}.)
*
* 这个方法返回的是在前与这个key联系在一起的value,或者是null,当没有value与这个key联系
* 在一起时。返回空值也能表明这个key实现与一个空值联系。
*/
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
/**
* Computes key.hashCode() and spreads (XORs) higher bits of hash
* to lower.
Because the table uses power-of-two masking, sets of
* hashes that vary only in bits above the current mask will
* always collide. (Among known examples are sets of Float keys
* holding consecutive whole numbers in small tables.)
So we
* apply a transform that spreads the impact of higher bits
* downward. There is a tradeoff between speed, utility, and
* quality of bit-spreading. Because many common sets of hashes
* are already reasonably distributed (so don't benefit from
* spreading), and because we use trees to handle large sets of
* collisions in bins, we just XOR some shifted bits in the
* cheapest possible way to reduce systematic lossage, as well as
* to incorporate impact of the highest bits that would otherwise
* never be used in index calculations because of table bounds.
*
* 计算key.hashCode()并将哈希的较高位扩展(XOR)到较低位。 因为该表使用2的幂次掩码,
* 所以仅在当前掩码上方的位中变化的哈希集将始终发生冲突。 (众所周知的示例是在小表中保存连续
* 整数的Float键集。)因此,我们应用了一种将向下扩展较高位的影响的变换。 在速度,实用性和
* 位扩展质量之间需要权衡。 由于许多常见的哈希集已经合理分布(因此无法从扩展中受益),并且由
* 于我们使用树来处理容器中的大量冲突集,因此我们仅以最便宜的方式对一些移位后的位进行XOR,
* 以减少系统损失, 以及合并最高位的影响,否则由于表范围的限制,这些位将永远不会在索引计算中
* 使用。
* 事实上我不懂上面这一段说的什么。。。,说的应该就是为什么要用下面这种算法的理由吧
* 这个方法就是说的是给定一个key,试怎么算出它的hash值的。
*
*
*/
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
/**
* Returns a hash code for an {@code int} value; compatible with
* {@code Integer.hashCode()}.、
* 这个是Integer的hashcode()方法
*
*
*
* @param value the value to hash
* @since 1.8
*
* @return a hash code value for an {@code int} value.
*/
public static int hashCode(int value) {
return value;
}
/**
* Implements Map.put and related methods.
*
* @param hash hash for key
*
* @param key the key
* @param value the value to put
* @param onlyIfAbsent if true, don't change existing value
* 如果onlyIfAbsent为true的话,不改变已经存在的值
* @param evict if false, the table is in creation mode.
* evict如果是false的话,table是创建模式
* @return previous value, or null if none
*/
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;
//如果table是空,或者table的长度为空,就resize,
/*
* 关于resize
* Initializes or doubles table size.
If null, allocates
* in accord with initial capacity target held in field
* threshold. Otherwise, because we are using power-of-two
* expansion, the elements from each bin must either stay
* at same index, or move with a power of two offset in
* the new table.
*
* 初始化或增加表大小。 如果为空,则根据字段阈值中保持的初始容量目标进行分配。 否则,
* 因为我们使用的是2的幂,所以每个bin中的元素必须保持相同的索引,或者在新表中以2
* 的幂偏移。
*
* */
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
//tab[i = (n - 1) & hash]这一块就是根据key的hash值与table的长度减一
//进行运算,得到要放入的table桶序号,如果这个桶中没有节点,则直接放入桶的第一个位置
else {
//如果得到的tab【i】是有节点的
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
//如果传入的hash与p的hash一样,传入的key与p的key一样,则把p赋给e(为什么这么做)
//这么做是因为如果做了这个赋值,下面的elseif和else就不用执行了,而后面是在e上操作的,
//所以把p赋给e,相当于找到了这个节点,用e表示这个节点,p是辅助节点。
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
//如果hash和key不一样,检查p(这个桶最开头的节点)是不是treenode,是的话按照treenode
//的规则插入新节点
else {
//桶一样,hash和key不一样,不是tree节点,那就从p开始遍历
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;
}
//上面这个是检查是否遍历到了最后一个,如果是的话就在最后一个后面插入
//再检查bincount计数器是否大于等于7,是的话就转成红黑树并跳出for
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
//如果再遍历过程中遇到一个hash和key都相等的节点,就跳出
p = e;
//这是让p等于p.next 相当于让p后移一个
}
}
//经过上面的代码,要么已经插入完成(桶一样,hash和key不一样,是tree节点就用tree节点
//的方法插入了,不是tree节点就遍历到了这个桶的最后,在桶的最后插入了,这两种情况下e都为null),
//要么是找到了key一样的节点(可能是桶的第一个节点一样,或者是不是树节点,在遍历中途找到了key
//一样的节点),并把它指向了e。
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
//上面是在已经存在这个key的情况下,根据onlyifabsent 判断是否修改value的值。
//并返回旧的value,在修改了value的值后,这儿为啥没有modCount++呢?
}
}
++modCount;	//为了实现fast-file机制
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}

最后

以上就是轻松水池为你收集整理的jdk13 HashMap中put方法源码分析的全部内容,希望文章能够帮你解决jdk13 HashMap中put方法源码分析所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(32)

评论列表共有 0 条评论

立即
投稿
返回
顶部