大家好,我是IT修真院郑州分院第十期的学员,一枚正直纯洁善良的java程序员
今天给大家分享一下,修真院官网java任务十,深度思考中的知识点——HashMap浅析。
背景介绍
Map集合即K-V集合,加上一个hash,这样是散列、无序的。HashMap简单理解就是散列、无序的K-V集合,接下来深入解析一下HashMap。
知识剖析
HashMap的数据结构
数组的优缺点:通过下标索引方便查找,但是在数组中插入或删除一个元素比较困难。
链表的优缺点:由于在链表中查找一个元素需要以遍历链表的方式去查找,而插入,删除快速。因此链表适合快速插入和删除的场景,不利于查找。
HashMap的底层是哈希数组,数组元素为Entry。HashMap通过key的hashCode来计算hash值,当hashCode相同时,通过“拉链法”解决冲突。
HashMap 的实例有两个参数影响其性能:“初始容量” 和 “加载因子”。容量 是哈希表中桶的数量,初始容量 只是哈希表在创建时的容量。加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要重建该哈希表内部数据结构,从而哈希表将具有大约两倍的桶数。
通常,默认加载因子是 0.75, 这是在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查询成本(在大多数 HashMap 类的操作中,包括 get 和 put 操作,都反映了这一点)。在设置初始容量时应该考虑到映射中所需的条目数及其加载因子,以便最大限度地减少重构操作次数。如果初始容量大于最大条目数除以加载因子,则不会发生重构操作。
常见问题
为什么HashMap的数组长度一定是2的次幂?
扩展思考
Jdk1.7和1.8中hashMap的区别
数据结构引入了红黑树,解决链表过长时增删改查效率过低的问题;
扩容后存储位置的计算不同,jdk1.7在扩容后,key需要按照原来的方法重新计算一次。1.8里,直接原位置或者原位置+扩容量;
插入数据的方法也不同,1.7使用的是头插法,1.8使用尾插法。
参考文献
https://blog.csdn.net/soga613/article/details/78958642
https://blog.csdn.net/liyantianmin/article/details/79401854
https://www.cnblogs.com/chengxiao/p/6059914.html
更多讨论
Q1:hashmap 超过默认长度扩容时,是重新创建hashmap(包括定义长度)然后将原来的内容复制过来,然后销毁原来的hashmap?
A1:对,因为数组这种数据结构只能重新申请内存空间,无法直接扩容。
Q2:HashMap什么时候进行扩容?
A2:根据负载因子计算,当前使用的的容量大于负载因子和最大容量的乘积时,进行扩容。
Q3:随着数据量的增加,hashMap的查询时间复杂度?
A3:数组时是O(1),链表时是O(log n),链表长度超过8个变为红黑树,是O(n)。