数据结构是java学习中很重要的一部分,HashMap也是数据结构中很常用的一部分。我们将通过源码对HashMap的方法和结构进行分析和理解。
为了能让我的理解和源码能更明白清晰的呈现出来,我将使用代码和解析一起的方式展示给大家。另外,这只是我个人的理解和分析,大家可以先看一下再去找更清晰准确的文章进行深入理解。我看到一篇不错的文章就链接给大家吧。我目前仅仅分析了基本的put()方法的源码,至于红黑树,链表之后再给大家详细分析。
转自zju_jzb的博客
//HashMap的put方法源代码以及个人理解解析
/*
* 基本思想:
* 在hashmap中底层是数组+链表式存储
* 先是按照key值进行hash作为存储地址在结构中进行寻找
* 找到后先看该地址是否为空,如果为空则直接存储就可以
* 如果不为空,就表示该地址已经存储元素,就需要进行覆盖
*
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict)
{
Node<K,V>[] tab; Node<K,V> p; int n, i;
/*
* 定义变量,tab表示节点数组,是哈希桶数组(可以用来存储hashmap
* 对应的key,value对节点元素)
* p表示节点;n表示数组长度;i表示数组下标;
*/
if ((tab = table) == null || (n = tab.length) == 0)
/*
* table实际上也代表节点数组
* 该语句判断数组是否为空,或者长度为0
*/
n = (tab = resize()).length;
//如果是那么重新定义数组的长度,resize表示重新定义长度
if ((p = tab[i = (n - 1) & hash]) == null)
/*
* 该语句使用i = (n - 1) & hash的位运算方式来确定数组下标,
* 将这个下标位置的节点元素赋值给p,判断p是否为空
* (实际也就是判断要插入的元素hash后的地址是否已存在元素)
*/
tab[i] = newNode(hash, key, value, null);
//如果为空表示该地址没有存储元素,可以直接创建一个新的节点元素进行存储
else {
/*否则表示该地址已经存在元素,
* 以及当两者key值相等时执行覆盖操作
*/
Node<K,V> e; K k;
/*
* 定义变量,e表示节点元素,k从下面k = p.key语句可以看出表示p的key值
*/
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
/*
* 判断hash值是否相同,并且同时判断key是否equals
*/
e = p;
//如果满足那么先将p节点元素赋值给e,
//因为后面可能还会对相同key值的节点进行操作,
//我们放到最后再进行覆盖或者链表,红黑树插入
***//下面是红黑树节点***
else if (p instanceof TreeNode)
/*
* 判断p的是否是TreeNode类型的节点对象
*/
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
/*
* 如果是
*/
***//下面是链表节点***
else {
for (int binCount = 0; ; ++binCount) {
/*
* binCount充当计数器
*/
if ((e = p.next) == null) {
/*
* 判断如果p的下一个元素是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
/*
* 如果key值相同。接下来就要进行覆盖
* 在e节点不为空的前提下进行下列操作
*/
V oldValue = e.value;
//变量创建及赋值,将e节点的value值赋值给变量oldValue
if (!onlyIfAbsent || oldValue == null)
/*
* 判断是否进行覆盖操作,
* onlyIfAbsent实际上就是控制是否进行覆盖操作,
* 如果为true不进行覆盖,false进行覆盖
* 当onlyIfAbsent为false或者oldValue为null时进行下面操作
*/
e.value = value;
//进行覆盖操作
afterNodeAccess(e);
//空函数
return oldValue;
//返回oldValue
}
}
++modCount;
if (++size > threshold)
/*
* 判断是否还能装的下,是否应该扩容
*/
resize();
/*
* 如果是,那么进行扩容
*/
afterNodeInsertion(evict);
return null;
//返回空值。
}