网站首页 > 文章精选 正文
1.谈一下HashMap的特性?
1.HashMap存储键值对实现快速存取,允许为null。key值不可重复,若key值重复则覆盖。
2.非同步,线程不安全。
3.底层是hash表,不保证有序(比如插入的顺序)
2.谈一下HashMap的底层原理是什么?
底层原理:Map + 无序 + 键唯一 + 哈希表 (数组+Entry)+ 存取值
1、HashMap是Map接口的实现类。实现HashMap对数据的操作,允许有一个null键,多个null值。 ConcurrentHashmap、Hashtable不支持key或者value为null,而HashMap是支持的。
2、是无序的集合,LinkedHashMap是有序的集合。
3、哈希表结构可以保证键唯一。
4、HashMap底层就是一个哈希表结构,数组+链表+红黑树(链表超过8个就用红黑树)。新建一个
HashMap的时候,就会初始化一个数组,数组的初始容量为16,数组中的每一项又是一个链表,
Entry就是数组中的元素,每个Entry其实就是一个key-value的键值对,它持有一个指向下一个元
素的引用next,这就构成了链表。先把元素按照相同的hash值进行分组,再把相同哈希值的元素挂
到一起。
5、hash算法决定在数组中的位置,equals方法决定在链表中的位置。当需要存储一个Node对象时
,会根据hash算法来决定在其数组中的位置,在根据equals方法决定其在该数组位置上的链表中的
存储位置;当需要取出一个Node对象时,也会根据hash算法找到其在数组中的存储位置, 在根据
equals方法从该位置上的链表中取出Node。
3 HashMap 的数据结构?
JDK1.7及之前:数组+链表
JDK1.8:数组+链表+红黑树
4 HashMap,LinkedHashMap,TreeMap 有什么区别?
HashMap 数据结构以数组为主,查询非常快
TreeMap 数据结构以红黑树为主,利用了红黑树左小右大的特点,可以实现 key 的排序,
LinkedHashMap 在 HashMap 的基础上增加了链表的结构,实现了插入顺序访问和最少访问
5谈一下hashMap中put是如何实现的?
1.计算关于key的hashcode值(与Key.hashCode的高16位做异或运算)
2.如果散列表为空时,调用resize()初始化散列表
3.如果没有发生碰撞,直接添加元素到散列表中去
4.如果发生了碰撞(hashCode值相同),进行三种判断
4.1:若key地址相同或者equals后内容相同,则替换旧值
4.2:如果是红黑树结构,就调用树的插入方法
4.3:链表结构,循环遍历直到链表中某个节点为空,尾插法进行插入,插入之后判断链表个数是
否到达变成红黑 树的阙值8;也可以遍历到有节点与插入元素的哈希值和内容相同,进行覆盖。
5.如果桶满了大于阀值,则resize进行扩容
6.HashMap中什么时候需要进行扩容,扩容resize()又是如何实现的?
(1)扩容时机:
1 初始化数组(jdk1.8之后,默认在第一次插入数据的时候才进行数组初始化,初始化
是通过调用扩容方法实现的);
2 当元素个数超过临界值(临界值=装载因子*数组容量);
3 当链表长度超过默认阈值8,尝试转化为红黑树,但发现数组长度不到64时。
(2)扩容机制:
1 如果数组未初始化过,会将数组的容量和装载因子都设置为默认值,并将数组创建出来。
2 如果数组初始化过,扩容会分配一个新的数组,新的数组长度翻倍,然后遍历整个老结构将元素
重新哈希映射到新的数组里。
3 HashMap在进行扩容时,使用的重新哈希的方式非常巧妙,因为每次扩容都是翻倍,与原来计算的
(数组长度-1)&hash 的结果相比,只是多了一个bit位,所以结点要么就在原来的位置,要么就被
分配到"原位置+旧容量"这个位置。
7.什么是哈希冲突以及 哈希冲突的解决方案
当要存储一个数据的时候,首先用一个函数计算数据的地址,然后再将数据存进指定地址位置的数组里面。这个函数就是哈希函数,而这个数组就是哈希表。
哈希冲突是指哈希函数算出来的地址被别的元素占用了
1.开放定制法
(1)线性探测
按顺序决定值时,如果某数据的值已经存在,则在原来值的基础上往后加一个单位,直至不发生哈希冲突。
(2)再平方探测
按顺序决定值时,如果某数据的值已经存在,则在原来值的基础上先加1的平方个单位,若仍然存在则减1的平方个单位。随之是2的平方,3的平方等等。直至不发生哈希冲突。
(3)伪随机探测
按顺序决定值时,如果某数据已经存在,通过随机函数随机生成一个数,在原来值的基础上加上随机数,直至不发生哈希冲突。
2.链地址法
对于相同的值,使用链表进行连接。使用数组存储每一个链表。
优点:
(1)拉链法处理冲突简单,且无堆积现象,即非同义词决不会发生冲突,因此平均查找长度较短;
(2)由于拉链法中各链表上的结点空间是动态申请的,故它更适合于造表前无法确定表长的情况;
(3)开放定址法为减少冲突,要求装填因子α较小,故当结点规模较大时会浪费很多空间。而拉链法中可取α≥1,且结点较大时,拉链法中增加的指针域可忽略不计,因此节省空间;
(4)在用拉链法构造的散列表中,删除结点的操作易于实现。只要简单地删去链表上相应的结点即可。 缺点:
指针占用较大空间时,会造成空间浪费,若空间用于增大散列表规模进而提高开放地址法的效率。
3.公共溢出区法
建立一个特殊存储空间,专门存放冲突的数据。此种方法适用于数据和冲突较少的情况。
4.再散列法
准备若干个hash函数,如果使用第一个hash函数发生了冲突,就使用第二个hash函数,第二个也冲突,使用第三个……
重点了解一下开放定制法和链地址法
8.谈一下当两个对象的hashCode相等时会怎么样?
会产生哈希碰撞,若key值相同则替换旧值,不然链接到链表后面,链表长度超过阙值8就转为红黑树存储
9.传统hashMap的缺点(为什么引入红黑树?)
JDK 1.8 以前 HashMap 的实现是 数组+链表,即使哈希函数取得再好,也很难达到元素百分百均匀分
布。当 HashMap 中有大量的元素都存放到同一个桶中时,这个桶下有一条长长的链表,这个时候
HashMap 就相当于一个单链表,假如单链表有 n 个元素,遍历的时间复杂度就是 O(n),完全失去了
它的优势。针对这种情况,JDK 1.8 中引入了 红黑树(查找时间复杂度为 O(logn))来优化这个问题。
10 加载因子为什么是 0.75?
那加载因子为什么是 0.75 而不是 0.5 或者 1.0 呢?
首先如果加载因子比较大,那么扩容发生的频率就比较低,但是他浪费的空间比较小,不过发生hash
冲突的几率就比较大,比如加载因子是1的时候,如果hashmap长度为128,那么可能hashmap的实际存
储元素数量在64至128之间的时间段比较多,而这个时间段发生hash冲突就比较大,造成数组中其中一
条链表较长,就会影响性能。而当加载因子值比较小的时候,扩容的频率就会变高,因此会占用更多的
空间,但是元素的存储就比较稀疏,发生哈希冲突的可能性就比较小,因此操作性能会比较高,比如设
置成0.5,同样128长度的hashmap,当数量达到65的时候就会触发hashmap的扩容,扩容后长度为256,
256里面只存储了65个似乎有点浪费了。所以综合了以上情况就取了一个 0.5 到 1.0 的平均数 0.75
作为加载因子。
猜你喜欢
- 2025-03-18 系统性能优化与Java代码编写性能考虑
- 2025-03-18 面试必问之:Java 中 == 和 equals 的区别你知道吗
- 2025-03-18 为什么重写 equals时必须重写 hashCode 方法?
- 2025-03-18 一网打尽-HashMap面试题(面试hashmap底层实现原理)
- 2025-03-18 HashMap面试知识点合集,这一篇就够了
- 2025-03-18 Java并发系列 | ConcurrentHashMap源码分析
- 2025-03-18 ConcurrentHashMap的实现原理(JDK1.7和JDK1.8)
- 2025-03-18 HashMap底层实现原理以及线程安全实现
- 2025-03-18 不怕面试再问HashMap,一次彻底地梳理(原理+手写实现)
- 2025-03-18 炸裂,大神图解JDK容器三大将之——哈希表(HashMap)
- 最近发表
- 标签列表
-
- newcoder (56)
- 字符串的长度是指 (45)
- drawcontours()参数说明 (60)
- unsignedshortint (59)
- postman并发请求 (47)
- python列表删除 (50)
- 左程云什么水平 (56)
- 计算机网络的拓扑结构是指() (45)
- 稳压管的稳压区是工作在什么区 (45)
- 编程题 (64)
- postgresql默认端口 (66)
- 数据库的概念模型独立于 (48)
- 产生系统死锁的原因可能是由于 (51)
- 数据库中只存放视图的 (62)
- 在vi中退出不保存的命令是 (53)
- 哪个命令可以将普通用户转换成超级用户 (49)
- noscript标签的作用 (48)
- 联合利华网申 (49)
- swagger和postman (46)
- 结构化程序设计主要强调 (53)
- 172.1 (57)
- apipostwebsocket (47)
- 唯品会后台 (61)
- 简历助手 (56)
- offshow (61)