-
与HashTable的区别
非同步,可为null
-
HashMap的性能影响因子
初始容量(buckets的数量)和加载因子(扩容,时间和空间的计算)
-
同步方法
Map m = Collections.synchronizedMap(new HashMap(...));
-
HashMap为什么要使用数组+链表(大于8转成红黑树)
基于时间和空间的考虑:
- 时间:数组查询快,链表插入快
- 空间:数组内存区域连续,指定大小,具有随机访问性;链表内存可以存在任何地方,每个数据都保存了下一个数据的内存地址(所以查找必须从第一个开始遍历,查找效率低)。
-
HashMap7出现闭环的原因
丢失和闭环均是由于改变链表方向导致的。
-
HashMap的get
- 获得key的hash,然后根据hash和key按照插入时候的思路去查找get。
- 如果数据位置为null,则直接返回null。
- 如果数组位置不为null,则先看查找到的当前数组位置的数据的key与查找key是否一致。
- 如果不一致则进一步判断是否是红黑树类型,是的话则按照红黑树类型查找返回。
- 如果不是红黑树并且数据有next,则按照遍历链表的方式查找返回。
-
HashMap的put
- 对数据进行hash值计算。
- 将数据插入前先检查当前table是否为空,如果为空则调用reSize方法进行初始化。
- 通过位运算获得key的目标位置,并判断当前位置情况。
- 如果当前位置为空则直接进行放置,如果跟当前key一致则进行覆盖。
- 如果当前有数据则看当前数据类型是否是红黑树,是的话需要调用putTreeVal。
- 如果不是红黑树则认为是个链表,然后再循环的查找进行尾部插入,同时还要考虑当前链表转红黑树。
- 对找到的旧节点进行判断,如果当前值为null,则直接替换。否则根据onlyIfAbsent是否为false判断是否进行替换。
- 以上步骤都进行完毕后对修改后的变量modCount加1,同时看最新的总结点。
-
HashMap的resize
- 获得当前table数据,如果table当前数据足够大则不再进行扩容(MAXIMUM_CAPACITY),只调节阈值。
- 如果table当前数据扩容后的范围符合要求,则直接将容器大小跟阈值都扩容。
- 如果是带参构造函数则需要将阈值复制给容器容量。
- 否则认为该容器初始化未传参,需要初始化。
- 如果老table有数据则创建新容器并扩容,数据转移。
- 数据不为空的单独的节点直接重新hash分配新位置。
- 数据不为空,但是后面为链表的,则需要把链表数据进行区分来分开转移。
- 数据不为空,但是该节点是红黑树的,则调用spit。
-
HashMap的remove
查找table[i]是否存在,是否在首节点上,是否在红黑树上,是否在链表上,找到了则直接删除,同时注意平衡性。
-
7 VS 8
- 7中找
Hash
用了4次,8中只用了1次。 - 7 = 数组 + 链表,8 = 数组 + 链表 + 红黑树
- 7中是头插法,多线程容易造成环,8中是尾插法。
- 7的扩容是全部数据重新定位,8中是位置不变+ 移动旧size大小来实现更好些。
- 7是先判断是否要扩容再插入,8中是先插入再看是否要扩容。
- HashMap非线程安全。
- 7中找
HashMap
最新推荐文章于 2020-11-04 15:22:47 发布