**
带你了解HashMap
**
HashMap的结构
在面试的时候常常会问到HashMap的结构,这时候是不是想到他是一个键值对结构呢? 具体怎么做的下面就直接开门见山:
- 直接在你的idea上建个类写个HashMap ctrl+鼠标右键点进去就可以看到HashMap的构造;
这个是HashMap的核心[Node链表对象] ,
再往下拖可以看到
这个是HashMap的存储容器 也就是一个数组,只看到这两点就行行 ,HashMap的结构就可以出来了(具体的存取在下面说),HashMap的结构也就是这个类和这个数据组成而来 一个数组链表。
HashMap的存操作
1.这里就不直接给大家看HashMap的原源代码我给大家看一个我注释过的代码(具体的一些值和红黑树操作就不去解释了)
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
/**
* put 方法具体调用
*
* @param key 计算出来的 hash 值
* @param key key
* @param value value
* @param onlyIfAbsent 如果true,则不替换原来的值
* @param evict LinkedHashMap 才用到.
* @return 返回 null 或者替换前的值
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
// table 为空或者长度为0,进行扩容操作
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);
// 如果当前索引有值了,说明hash碰撞了
else {
Node<K,V> e; K k;
// p 是该索引位置的链表的第一个节点
// hash 值相等,key 又相等,说明是同一个key, 替换value
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);
// 不是红黑树,key 之前又不存在
else {
for (int binCount = 0; ; ++binCount) {
// p.next == null 说明是最后一个节点了
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
// 链表长度大于8,转化成红黑树操作
// 如果tab.length 达不到 MIN_TREEIFY_CAPACITY,是不会转化的
// -1 是因为上面已经排除了首节点了
if (binCount >= TREEIFY_THRESHOLD - 1)
treeifyBin(tab, hash);
break;
}
// 在非首节点中已存在,则替换value
if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
// e 是上面操作,得到的key存在的那个节点,不为null则说明存在,已存在则替换value
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
//增加size,并判断是否要扩容,size 大于扩容阀值,则要扩容
if (++size > threshold)
resize();
//这个方法在LinkedHashMap中有实现
afterNodeInsertion(evict);
return null;
}
大家只有要认汉字没问题和是同行的几乎都可以在我的备注以及代码中看得出来这个存操作的过程了
(为了方便不愿看代码的小伙伴 此处还是用纯文字的方式讲一遍存过程):
当HashMap进行存储的时候会先判断-数组table 如果为空或者长度为0进行扩容操作 接下来对Key求Hash值,然后再计算下标,如果hash碰撞了(碰撞的意思是计算得到的Hash值相同),如果碰撞了,就放到该下标下的链表后面,如果没有碰撞,直接放入数组中声明为该下表的链表的头结点,如果链表长度超过阀值( TREEIFY THRESHOLD==8),就把链表转成红黑树,链表长度低于6,就把红黑树转回链表,如果节点已经存在就替换旧值,再接下来就是对size的操作 如果桶满了(容量16*加载因子0.75),就需要 resize(扩容2倍后重排)
HashMap的取操作
1.同样以一份自己注释过的代码开始
当调用取值方法时 第一步先判断指定的下班位置有没有值 如果没值直接就返回null,有值时 先判断首节点 如果首节点的key就对应上了就返回改node对象,如果不对 接下来判断是否是红黑树操作 如果是就执行红黑树操作 ,不是的话就遍历链表查找找到返回。
HashMap先讲到这里 下次在带来更详细更深入的技术讲解(实际老板催写代码了)