HashMap Note [vaynexiao]

1,常量
DEFAULT_INITIAL_CAPACITY = 1 << 4; // 初始容量16,即默认数组长度
MAXIMUM_CAPACITY = 1 << 30; // 最大容量
DEFAULT_LOAD_FACTOR = 0.75f; // 负载因子
TREEIFY_THRESHOLD = 8; // 树化阈值
UNTREEIFY_THRESHOLD = 6; // 树退化阈值
MIN_TREEIFY_CAPACITY = 64; // 最小树化容量
(在转变成树之前,还会有一次判断,只有 capacity 大于 64 才会发生转换。
这是为了避免在哈希表建立初期,多个键值对恰好被放入了同一个链表中而导致不必要的转化)

2,HashMap 数据结构
A:数组+单链表,1.8以后又加了红黑树,当链表节点个数超过默认值8个后,进行树化,
使用红黑树一个综合取优的选择,相对于其他数据结构,红黑树的查询和插入效率都比较高。
而当红黑树的节点个数小于默认值6个以后,又开始使用链表。
一个8,一个6,是为了防止频繁在链表和红黑树之间切换。

3,size的获取效率怎么样
A: 每次插入一个元素时候就会给属性size++,所以获取效率极高

4,put方法返回什么? 相同key时value会怎样?
A: 返回的是根据key找到上一次该key的value,如果没有就是null;value会覆盖。

5,HashMap是否支持key为null?
A:null无法调用hashCode方法,也就无法确定桶下标,强制指定一个桶下标0来存放。

6,如果单链表节点个数超过阈值8,是否一定会树化?
A:不一定,超过阈值8且容量CAPACITY超过64 才会树化。(默认值64可以修改)
如果容量不足64,会先去判断是否需要扩容,满足扩容条件,直接扩容,不会树化,
因为扩容不仅能增加容量,还能缩短单链表的节点数。

7,对象相同,哈希值一定相同吗? 哈希值相同,对象一定相同吗?
A:对象相同,哈希值一定相同;哈希值相同,只能证明一定在同一个数组位置上,同一个index

8,HashMap扩容的原因? 什么时候扩容? 怎么扩容?

return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);

为了提升HashMap的get、put等方法的效率,如果不扩容,链表或者红黑树就会越来越长,导致插入和查询效率都会变低;
当元素个数 大于 容量*0.75时扩容;
满足扩容条件后,会将原来元素重新计算hash值,放入一个扩大2倍的数组中,有个规律:新位置要么原坐标,要么加上扩大的数量,
因为数组扩大后长度的二进制数的左侧会多出一个1,hash值进行异或运算时就自然增加了扩大的数量。
比如:
原始hashcode值: 0000 1000
新数组长度数的二进制:0001 0000

9,HashMap 扩容和插入的顺序? 插入元素方式? 死循环?
A:1.7以前是先扩容再插入,1.8以后是先插入再扩容。
1.8考虑的是:如果插入的key已经存在,只需要替换value,这样可以避免一次无用扩容
1.7以前是头插法,虽然效率高,但很有可能发生两个元素之间循环链。
1.8以后是尾插法,避免了循环问题。具体不同。

10,hash值如何计算(数组下标如何确定)? 为什么使用&? 为啥不使用取余?
A:对Key进行hash,得到hash值,然后用hash值对HashMap的容量进行与运算 h & (length-1)
当数组长度为2的n次方时,减去1再与hash值 与运算,不同的key计算得出的index索引刚好都会在 0 - capacity 之间
相同的几率才会较小,数据在数组上分布也比较均匀,碰撞的几率也小
取余也是得到一个index,但是与运算速度要快很多,而且取余后是负数也需要额外处理。

11,put流程

1,执行hash(key)得到一个int类型的hash值,然后异或运算,得到应该插入到数组中的位置;
2,判断table是否为空,为空表明这是第一个元素插入,则使用resize()进行初始化,初默认大小16;
3,如果该位置为空,说明没有产生hash碰撞,则直接插入,调到第5步,否则第4部;
4,如果该位置不为空,进行下面三种判断:

  • 1.循环遍历该位置的链表上每一个元素,发现有相同key元素,直接替换value即可;
  • 2.没有相同key时,尾插法添加元素,再判断链表长度,决定是否树化;

5,判断新插入这个值是否导致size已经超过了阈值,是则进行扩容

猜你喜欢

转载自blog.csdn.net/vayne_xiao/article/details/110921093