简单谈谈对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

  减小加载因子,虽然降低了空间利用率,但是也降低了哈希碰撞

 

posted @   最强Java代表  阅读(722)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示