简单谈谈对HashMap的理解
——谈谈你对HashMap的认识吧。
HashMap是基于哈希表的Map接口实现的,以key-value的形式存储,主要是存放键值对,HashMap的实现不是同步的,所以是线程不安全的,并且它的key,value可以为null,当key为null时,value存储在数组下标为0的位置,并且HashMap的映射也不是有序的
HashMap维护了一个数组,数组的每一个元素都是一个Entry对象,每个Entry对象都包含4个属性:key,value,hash,next,其中next是对下一个元素的引用,默认是null
当我们向HashMap中插入一个元素的时候,使用key通过哈希函数得到一个hash值,根据hash值能得到一个数组下标,如果得到的数组下标的位置为空,就插入数据,如果位置上有其他的Entry对象,就会发生哈希碰撞,发生了哈希碰撞,就要把插入的Entry对象跟这个位置上的其他Entry对象通过next属性连接起来形成一个链表
JDK1.8之前,采用的是数组+链表,链表主要解决了哈希碰撞(两个对象调用hashCode()的哈希码值一致导致数组索引相同)而存在的,链表的优点在于增加,删除速度快,但是查询效率低
JDK1.8之后,采用的是数组+链表/红黑树,之所以改为红黑树,原因是为了提高查询效率
——jdk1.8为什么要引入红黑树?
红黑树也叫平衡二叉查找树
二叉树的特点就是任意节点的左子树的所有节点都小于它,任意节点的右子树的所有节点都大于它,红黑树在这个基础上做了调整,保证左子树和右子树的高度差最大为2
——遍历链表和红黑树的时间复杂度各是多少?
遍历链表的时间复杂度是O(n),遍历红黑树的时间复杂度是O(logn)
HashMap根据key查找的时间复杂度只取决于桶里的数据结构
——HashMap什么时候用链表,什么时候用红黑树?
数组上某个下标位置的节点数目大于8时,并且数组长度大于64时,会从链表转为红黑树,当节点数目小于等于6时,会从红黑树转为链表
当数组上某个下标位置的节点数目大于8时,但是数组长度小于64时,不会从链表转为红黑树,因为当数组的长度小于64时,查询效率比较快,并且链表转红黑树会浪费资源并占用较大的空间
——哈希函数具体是怎么实现的?
想HashMap中插入一个Entry对象,会使用key调用hashCode()方法,得到一个int类型的HashCode,再将hashCode往右移16位,与hashCode本身做异或运算,得到hash值
——怎么使用hash值计算数组下标?
使用hash值和[数组长度-1]做位与运算,得到一个0-[数组长度-1]的数,这个数就是插入位置的下标
HashMap的数组长度一定是2的n次幂,所以 [数组长度-1] 换算成二进制的每一位都是1
——为什么jdk1.8中链表的插入方式要改为尾插入?
如果采用头插法的话,在并发场景下,扩容可能会产生循环链表的情况
——你知道HashMap的扩容机制吗?
HashMap容器的容量是16,加载因子默认是0.75
当容器中的元素数量大于 容量 * 加载因子,就会进行扩容,每次扩容的HashMap的容量会扩大1倍(*2)
JDK1.8之前,扩容的核心思想:使用一个容量更大的数组代替原来的数组,将原来数组内的元素拷贝到新数组中,拷贝过程中会遍历原数组中的元素,依次添加到新数组中
JDK1.8之后,扩容的核心思想:将原数组的各个元素的hash值与原数组长度做位与运算,如果结果为0,则位置不用改变,如果位置不为0,则元素下标改为 原数组下标+原数组长度
——为什么加载因子默认是0.75?
加载因子越大,数组的空间利用率就越高,但是哈希碰撞的概率也会越高
加载因子为0.75是对空间利用率和哈希碰撞概率的折中
——如何在高并发的情况下使用HashMap?
使用ConcurrentHashMap 或者 synchronizedMap(Hashmap hashMap)
——ConcurrentHashMap与HashMap有什么区别?
HashMap是线程不安全的,ConcurrentHashMap是线程安全的
HashMap的key和value可以为null,ConcurrentHashMap的key和value不能为空
——ConcurrentHashMap是怎么保证线程安全的?
JDK1.8之前,采用了分段式锁,把一个HashMap拆成多个Segment容器,每个容器配一把锁,同一时间只允许一个线程持有这把锁,宏观上多个线程可以同时访问这个HashMap容器
JDK1.8之后,采用了HashMap的数组+链表/红黑树的结构,把头/根节点加上了synchronized锁,同一时间,只有一个线程可以对该链表/红黑树进行操作
——Hashtable与HashMap有什么区别?
HashMap是线程不安全的,HashTable是线程安全的
HashMap的key和value可以为null,HashTable的key和value不能为空
HashMap每次扩容数组长度都会*2,HashTable每次扩容数组长度都会*2+1,这么做是为了尽量是奇数,防止哈希碰撞
——那key为null的情况下,会把null存到什么位置?
存到数组下标为0的位置
——HashMap的key是有序存放的吗?
不是,是根据插入元素的hash值来计算插入位置的
——如果我想有序存放要怎么办?
使用LinkedHashMap或者TreeMap
LinkedHashMap维护了一个双向链表
TreeMap是红黑树,遍历TreeMap的时候会按照自然排序输出所有元素
——如果要用HashMap存一万条数据,怎么做能提高效率?
预定义存储空间 = 数据量/加载因子 + 1
减小加载因子,虽然降低了空间利用率,但是也降低了哈希碰撞
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)