1.HashMap的特性
- HashMap存储键值对(key : value) 实现快速存取,key跟value都允许为null,key只能有1个null
- 非线程安全
- 不能保证有序
2.HashMap的结构(jdk1.8)
HashMap采用数组 + 链表或红黑树,节点是用Node
3.HashMap的put方法执行过程
- 先对key执行hash方法运算 (注意当key为null的时候,hash方法会固定返回0,根据hash & (length -1)的算法判定存入的数据会保存在数组[0] 的位置)
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
- 如果散列列表为空的时候,即第一次执行put方法,会调用resize()生成一个长度为16的Node数组
newCap = DEFAULT_INITIAL_CAPACITY;//数组默认的初始长度16
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);//扩容阈值 加载因子0.75*16
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
- 如果没有发生hash碰撞,直接将数据根据 hash & (length -1)的算法插入到数组对应的位置
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
- 如果发生hash碰撞,则进行3种判断
- 若key相同且equals后的内容相同,则替换旧值
- 若插入的是红黑树结构,则调用树的插入方法
- 链表结构,会循环遍历链表节点,内容相同则替换旧值;无相同内容在链表尾部进行插入,插入后会判定链表的长度是否达到变更红黑树的阈值8,如果达到则调用
treeifyBin(tab, hash);
方法进行转化。
- 如果元素个数超过扩容阈值的时候,会进行resize()扩容。
if (++size > threshold)
resize();
4.HashMap的resize()调用场景以及实现过程
调用场景:
- 初始化数组table
- 当元素个数超过扩容阈值的时候
- 当链表转化为红黑树,但是数组长度小于64的时候
实现过程:
- 通过判定数组数组长度是否为0
- 是:初始化数组table,默认长度为16,如果用户通过构造函数指定了一个数字作为容量,那么Hash会选择大于该数字的第一个2的幂作为容量。(3->4、7->8、9->16)
否:进行扩容并扩容成两倍(在小于static final int MAXIMUM_CAPACITY = 1 << 30;
的情况下),对元素重新进行排序,还是按照原来的hash & (length -1)算法,length为原来的两倍。在与运算下,hash与的位数不同将导致元素可能还在原来的位置上或者在原来位置*2的位置上 (原来长度为16,则跟hash值与运算的是1111,扩容后,长度为32,则跟hash值与运算的是11111)。
关于HashMap的一些疑问?
- 为什么HashMap的长度要是2的n次方?
减少hash碰撞==》https://blog.csdn.net/eaphyy/article/details/84386313 - 为什么链表长度达到8的时候转为红黑树,不是其他数字?
https://blog.csdn.net/daiyuhe/article/details/89424736 - 为什么链表转为红黑树需要数组长度超过64?
个人理解:避免恶意hash碰撞,导致数组长度过小的情况下出现红黑树影响性能。
来源:CSDN
作者:tlynn_chen
链接:https://blog.csdn.net/TLynn_Chen/article/details/104059509