HashMap面试

1.HashMap的数据结构?

数组+链表实现,当链表长度超过8时,链表转换为红黑树

2.HashMap的工作原理?

底层是hash数组和单向链表实现,数组中每个元素都是链表,

K/V存储put的过程

1)调用hash(k)方法计算K的hash值,然后结合数组长度,计算数组下标;

2)调整数组大小(当容器的元素个数大于capacity * loadfactor时,容器会进行扩容resize为2n);

3)a)如果K的hash值在HashMap中不存在,则执行插入,若存在,则发生碰撞;

      b)如果K的hash值存在,而且两者equals返回true,则更新键值对;

      c)如果K的hash值存在,而且两者equals返回false,则插入链表的尾部(尾插法)或红黑树中。

获取对象,将K传给get()方法

1)调用hash(K)方法从而获取该键值所在链表的数组下标

2)顺序遍历链表,equals查找相同Node链表中K值对应的V值

总结:hashCode是定位的,存储位置;equals是定性的,比较两者是否相等

3.当两个对象的hashCode相同会发生什么?

因为 hashCode 相同,不一定就是相等的(equals方法比较),所以两个对象所在数组的下标相同,"碰撞"就此发生。又因为 HashMap 使用链表存储对象,这个 Node 会存储到链表中

4.hash如何实现?为什么要这样?

JDK1.8中,通过hashCode()的高16位异或低16位实现的:(h = k.hashCode()) ^(h>>>16),主要从速度、功效和质量来考虑的,减少系统的开销,也不会造成因为高位没有参与下标的计算,从而引起的碰撞。

5.为什么要用异或运算符?

保证对象的hashCode的32位值只要有一位发生改变,整个hash()返回值就会改变。尽可能减少碰撞。

6.HashMap的table的容量如何确定?loadFactor是什么?该容量如何变化?这种变化会带来什么问题?

1)table数组大小是由capacity这个参数确定的,默认16,也可以构造时传入,最大限制1<<30

2)loadFactor是装载因子,用来判断table数组是否要动态扩展,默认值是0.75,比如table大小为16,装载因子为0.75,当table实际大小超过12时就要动态扩容

3)扩容时,调用resize()方法,将table长度变为原来的两倍(是table的长度)

4)如果数据很大的情况下,扩展时将会带来性能的损失,在性能要求很高的地方,这种损失很可能很致命

7. 数组扩容的过程?

如果数据很大的情况下,扩展时将会带来性能的损失,在性能要求很高的地方,这种损失很可能很致命

8.拉链法导致的链表过深问题为什么不用二叉查找树代替,而选择红黑树?为什么不一直使用红黑树?

二叉树在特殊的情况下会变成一条先行结构,遍历查询会非常慢。而红黑树在插入新数据时可能需要左旋、右旋、变色这些操作来保持平衡,引入红黑树就是查找数据块,解决链表查询深度的问题,我们知道红黑树属于平衡二叉树,但是为了保持“平衡”是需要付出代价的,但是该代价所损耗的资源要比遍历线性链表要少,所以当长度大于8的时候,会使用红黑树,如果链表长度很短的话,根本不需要引入红黑树,引入反而会慢

9.说说对红黑树的见解?

1)每个节点非红即黑

2)根节点总是黑色的

3)如果节点是红色的,则它的子节点必须是黑色的(反之不一定)

4)每个叶子节点都是黑色的空节点(NIL节点)

5)从根节点到叶节点或空子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度)

10.HashMap、LinkedHashMap、TreeMap的区别?

1)LinkedHashMap:保存了记录的插入顺序,在用Iterator遍历时,选取到的记录肯定是先插入的;遍历比HashMap慢

2)TreeMap:实现SortMap接口,能够把它保存的记录根据键排序(默认按键值升序排序)

11. HashMap和HashTable有什么区别?

1)HashMap线程不安全,HashTable是线程安全的

2)由于线程安全,HashTable的效率比不上HashMap

3)HashMap最多只允许一条记录的键为null,允许多条记录的值为null,而HashTable不允许

4)HashMap 默认初始化数组的大小为16,HashTable 为 11,前者扩容时,扩大两倍,后者扩大两倍+1;

5)HashMap 需要重新计算 hash 值,而 HashTable 直接使用对象的 hashCode;

12.为什么ConcurrentHashMap比HashTable效率要高?

1)HashTable使用一把锁锁住整个链表结构来处理并发问题,多个线程竞争一把锁,容易阻塞

2)ConcurrentHashMap

    a)1.7中使用分段锁(ReentrantLock + Segment + HashEntry),相当于把一个HashMap分成多个段,每段分配一把锁,支持多线程访问。锁粒度:基于Segment,包含多个HashEntry。

    b)1.8中使用CAS+synchronized+Node+红黑树。锁粒度:Node(首节点)(实现Map.Entry)。锁力度降低了

12.针对ConcurrentHashMap锁机制具体分析(1.7 vs 1.8)

1.7中采用分段锁的机制,实现并发的更新操作,底层采用数组+链表的存储结构,包括两个核心的静态内部类Segment和HashEntry

1)Segment继承ReentrantLock(重入锁)用来充当锁的角色,每个Segment对象守护每个散列映射表的若干个桶;

2)HashEntry用来封装映射表的键值对

3)每个桶是由若干个HashEntry对象链接起来的链表

1.8中,采用Node + CAS + Synchronized来保证并发安全。取消类Segment,直接用table数组存储键值对;当HashEntry对象组成的链表长度超过TREEIFY_THRESHOLD时,链表转为红黑树,提升性能

13. ConcurrentHashMap在1.8中,为什么要使用内置锁Synchronized来替代重入锁ReentrantLock?

1)粒度降低了

2)JVM开发团队没有放弃synchronized,而且基于JVM的synchronized优化空间更大,更加自然

3)在大量的数据操作下,对于JVM的内存压力下,基于API的ReentrantLock会开销更多的内存

<think>好的,我现在需要处理用户关于HashMap面试题目的查询。首先,用户已经提供了四个引用来源,其中引用[1]、[2]、[3]、[4]都涉及Java集合框架,特别是HashMap的相关内容。我需要根据这些引用来整理出常见的问题和答案。 首先,我需要回顾每个引用的内容。引用[1]提到了TreeSet、区分重复元素的方法、ArrayList和Vector的区别,以及Collection和Collections的区别。引用[2]讨论了HashMap与Hashtable的区别,以及可能的刁钻问题。引用[3]详细描述了JDK1.8前后HashMap的底层实现,包括扰动函数、拉链法和红黑树。引用[4]则是关于Java集合的20个问题,但具体内容未详细列出,不过可能包含更多相关问题。 用户的需求是查找HashMap面试题及答案,因此需要重点从引用[2]、[3]中提取信息,并结合其他引用中的相关内容。例如,引用[3]详细解释了HashMap的底层结构、哈希冲突解决方法和红黑树的引入,这些都是常见的面试问题。引用[2]提到了HashMap与Hashtable的区别,这也是经典问题。 接下来,我需要整理出常见的问题,并确保每个问题都有对应的解答,同时正确引用来源。例如,问题可能包括: 1. HashMap和Hashtable的区别?答案应涉及null键值、同步性等,引用[2]和[3]。 2. JDK1.8中HashMap的改进?涉及红黑树,引用[3]。 3. 哈希冲突解决方法?拉链法和红黑树,引用[3]。 4. 扰动函数的作用?引用[3]。 5. 为何HashMap不保证顺序?引用[3]或常识。 6. Collection和Collections的区别?引用[1]。 7. 其他相关问题如初始容量、加载因子、线程安全等,可能需综合多个引用。 需要注意每个答案后正确标注引用来源,例如关于扰动函数的部分来自引用[3]。同时,用户要求使用中文回答,行内公式用$...$,独立公式用$$...$$,但根据问题内容可能公式较少,但需注意LaTeX语法正确。 另外,用户示例中的快速排序部分有代码块,这里可能需要针对HashMap的问题加入代码示例,如扰动函数的实现,或者红黑树相关的伪代码,但需确认引用内容中是否有提供。例如,引用[3]提到扰动函数是HashMap的hash方法,可以举例代码。 最后,生成相关问题部分需要基于已整理的问题,提出扩展性问题,例如性能优化、并发处理等,可能需要结合引用中的内容或常识。 现在需要确保每个问题解答准确,引用正确,格式符合要求,并且结构清晰。可能还需要检查是否有重复问题,或者是否覆盖了用户关心的主要方面。</think>### HashMap面试问题及解答 #### 1. HashMap与Hashtable的区别是什么? - **HashMap**允许键和值为`null`,而**Hashtable**不允许[^2]。 - **HashMap**是非线程安全的(非synchronized),而**Hashtable**的方法通过`synchronized`关键字保证线程安全[^2][^3]。 - **HashMap**在JDK1.8后引入红黑树优化链表过长的问题,而**Hashtable**无此机制。 #### 2. JDK1.8中HashMap的底层实现有哪些改进? JDK1.8前,HashMap使用**数组+链表**(拉链法)处理哈希冲突;JDK1.8后,当链表长度超过阈值(默认为8)时,链表会转换为**红黑树**,以提高查询效率(时间复杂度从$O(n)$降为$O(\log n)$)。 #### 3. HashMap如何解决哈希冲突? - **拉链法**:将哈希值相同的键值对存储在链表中。 - **红黑树优化**:当链表过长时转换为红黑树(JDK1.8引入)。 #### 4. 什么是扰动函数?它的作用是什么? 扰动函数是HashMap的`hash()`方法,通过将键的哈希码的高16位与低16位异或,减少哈希冲突。例如: ```java static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } ``` 其目的是让哈希码分布更均匀,减少低质量`hashCode()`实现的影响。 #### 5. HashMap的初始容量和加载因子是什么? - **初始容量**默认为16,**加载因子**默认为0.75。当元素数量超过`容量×加载因子`时触发扩容(容量翻倍)。 - 公式:扩容阈值$T = \text{capacity} \times \text{loadFactor}$。 #### 6. 为何HashMap不保证元素的顺序? 因为哈希函数将键分散到不同的桶中,且扩容时可能重新哈希元素,导致顺序变化。 #### 7. Collection和Collections的区别是什么? - **Collection**是集合框架的根接口(如List、Set)。 - **Collections**是工具类,提供静态方法操作集合(如排序、同步包装)[^1]。 #### 8. HashMap为何线程不安全? 多个线程同时扩容可能导致链表成环或数据丢失。线程安全替代方案: - 使用`ConcurrentHashMap`。 - 通过`Collections.synchronizedMap()`包装。 #### 9. HashMap的`put()`方法流程? 1. 计算键的哈希值。 2. 定位到数组索引。 3. 若桶为空,直接插入;否则遍历链表/红黑树: - 键存在则更新值。 - 键不存在则插入新节点。 4. 若链表长度≥8且数组长度≥64,链表转红黑树。 #### 10. 为何JDK1.8引入红黑树而非其他数据结构? 红黑树在增删查操作上综合性能较好(平均时间复杂度$O(\log n)$),且能避免链表过长导致的性能骤降。 --- ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值