HashMap常见问题整理

HashMap是一种常见的Java集合,基于Entry数组+链表或红黑树实现。本文深入探讨HashMap的实现原理,包括为什么使用数组+链表、解决hash冲突的方法、扩容条件、get/put过程、红黑树转换条件,以及并发问题和key的选择。分析了HashMap的优化策略,如2的次幂扩容和高16位异或低16位的“扰动函数”。此外,还讨论了使用可变类作为key可能带来的问题。
摘要由CSDN通过智能技术生成

一、HashMap实现原理

你看过HashMap源码嘛,知道原理嘛?

在这里插入图片描述
HashMap采用Entry数组来存储key-value对,每一个键值对组成了一个Entry实体,Entry类实际上是一个单向的链表结构,它具有Next指针,可以连接下一个Entry实体。
只是在JDK1.8中,链表长度大于8的时候,链表会转成红黑树!

为什么用数组+链表?

数组是用来确定桶的位置,利用元素的key的hash值对数组长度取模得到.
链表是用来解决hash冲突问题,当出现hash值一样的情形,就在数组上的对应位置形成一条链表

hash冲突你还知道哪些解决办法?

比较出名的有四种:开放定址法、链地址法、再哈希法、公共溢出区域法

  • 开放地址法
    从发生冲突的那个单元起,按照一定的次序,从哈希表中找到一个空闲的单元。然后把发生冲突的元素存入到该单元的一种方法。开放定址法需要的表长度要大于等于所需要存放的元素。

  • 再散列
    当发生冲突时,使用第二个、第三个、哈希函数计算地址,直到无冲突时。缺点:计算时间增加。

  • 链接地址法的思路是将哈希值相同的元素构成一个同义词的单链表,并将单链表的头指针存放在哈希表的第i个单元中,查找、插入和删除主要在同义词链表中进行。在java中,链接地址法也是HashMap解决哈希冲突的方法之一。

  • 公共溢出区域法
    将哈希表分为公共表和溢出表,当溢出发生时,将所有溢出数据统一放到溢出区。

我用LinkedList代替数组结构可以么?

答案很明显,必须是可以的。
既然是可以的,为什么HashMap不用LinkedList,而选用数组?
因为用数组效率最高!
在HashMap中,定位桶的位置是利用元素的key的哈希值对数组长度取模得到。此时,我们已得到桶的位置。显然数组的查找效率比LinkedList大。

既然是可以的,为什么HashMap不用LinkedList,而选用数组?

因为采用基本数组结构,扩容机制可以自己定义,HashMap中数组扩容刚好是2的次幂,在做取模运算的效率高。
而ArrayList的扩容机制是1.5倍扩容,那ArrayList为什么是1.5倍扩容这就不在本文说明了。

二、HashMap在什么条件下扩容

HashMap在什么条件下扩容

如果bucket满了(超过load factor*current capacity),就要resize。
load factor为0.75,为了最大程度避免哈希冲突
current capacity为当前数组大小。

为什么扩容是2的次幂

HashMap为了存取高效,要尽量较少碰撞,就是要尽量把数据分配均匀,每个链表长度大致相同,这个实现就在把数据存到哪个链表中的算法;这个算法实际就是取模,hash%length。
但是,大家都知道这种运算不如位移运算快。
因此,源码中做了优化hash&(length-1)。
也就是说hash%length==hash&(length-1)
位运算比较高效,当b为2的n次方时,有如下替换公式:
a % b = a & (b-1)(b=2n)
即:a % 2n = a & (2n-1)

为什么为什么要先高16位异或低16位再取模运算

hashmap这么做,只是为了降低hash冲突的几率。

比如,当我们的length为16的时候,哈希码(字符串“abcabcabcabcabc”的key对应的哈希码)对(16-1)与操作,对于多个key生成的hashCode,只要哈希

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值