面试题答案
一键面试HashSet的扩容机制
- 基于HashMap实现:在Java中,
HashSet
内部是通过HashMap
实现的。HashSet
的容量、负载因子等相关概念都依赖于HashMap
。 - 容量与负载因子:
- 容量(Capacity):指
HashMap
内部数组的大小,初始容量默认是16。 - 负载因子(Load Factor):默认值是0.75。负载因子表示
HashMap
在其容量自动增加之前可以达到多满的一种尺度。 - 阈值(Threshold):当
HashMap
中的键值对数量(size)达到阈值(阈值 = 容量 * 负载因子)时,就会触发扩容。例如,初始容量为16,负载因子为0.75,则阈值为16 * 0.75 = 12 。当HashSet
中元素个数达到12时,就会触发扩容。
- 容量(Capacity):指
- 扩容过程:
- 创建新数组:扩容时会创建一个新的数组,新数组的大小是原数组大小的2倍。例如原数组大小为16,扩容后变为32。
- 重新计算哈希值与迁移数据:将原数组中的元素重新计算哈希值,并根据新的哈希值重新分配到新数组的对应位置。这是因为数组大小改变后,元素的哈希值对应的数组索引位置可能会发生变化。
扩容操作对性能的直接影响
- 时间性能影响:
- 扩容瞬间性能损耗:扩容时需要创建新数组并重新分配元素,这涉及大量的计算和数据迁移操作,会导致在扩容瞬间性能急剧下降,时间复杂度较高。例如,在元素较多时,重新计算哈希值和迁移数据会消耗大量的CPU时间。
- 后续插入性能提升:扩容后,由于数组变大,哈希冲突的概率降低,后续插入元素时查找和插入操作的平均时间复杂度会降低,性能有所提升。例如,在哈希冲突较少的情况下,插入元素的时间复杂度更接近理想的O(1)。
- 空间性能影响:
- 空间占用增加:扩容后数组变大,会占用更多的内存空间。虽然这在一定程度上减少了哈希冲突,但如果元素数量没有持续增长,会造成内存空间的浪费。例如,扩容后可能有很多数组位置是空的,但依然占用了内存。