MST

星途 面试题库

面试题:Java HashMap扩容机制与其他集合类在性能影响方面的深度对比

将Java HashMap的扩容机制与诸如ConcurrentHashMap等其他类似集合类进行对比,详细分析它们在扩容机制上的差异,以及这些差异如何导致在不同场景(如高并发读、高并发写、大量数据插入等)下性能表现的不同,并且探讨如何根据具体业务场景选择合适的集合类以优化性能。
39.8万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

1. HashMap扩容机制

  • 扩容条件:当HashMap中的元素个数(size)达到容量(capacity)与负载因子(load factor,默认0.75)的乘积时,就会触发扩容。例如,初始容量为16,当元素个数达到16 * 0.75 = 12时,就会进行扩容。
  • 扩容过程:扩容时,会创建一个新的更大的数组,新容量为原来的2倍。然后将旧数组中的所有元素重新计算哈希值并放入新数组中。这个过程比较耗时,因为需要重新计算哈希和移动元素。

2. ConcurrentHashMap扩容机制

  • JDK 1.7
    • 分段锁机制:采用Segment数组和HashEntry数组实现,Segment继承自ReentrantLock。每个Segment管理一个HashEntry数组。
    • 扩容方式:当某个Segment中的元素达到一定阈值时,只对该Segment进行扩容,而不是整个ConcurrentHashMap。这样可以减少扩容时的竞争,提高并发性能。
  • JDK 1.8
    • 摒弃分段锁:采用Node数组 + 链表/红黑树结构,利用CAS和synchronized实现线程安全。
    • 扩容方式:当元素个数达到容量的0.75倍时触发扩容。扩容时采用了transfer方法,将数据迁移到新的数组。在迁移过程中,会使用多个线程并行进行数据迁移,大大提高了扩容效率。

3. 扩容机制差异导致的性能表现不同

  • 高并发读
    • HashMap:本身是非线程安全的,在高并发读场景下,如果没有外部同步机制,可能会出现数据不一致的问题。
    • ConcurrentHashMap:无论是JDK 1.7还是1.8,都支持高并发读。在JDK 1.7中,读操作基本无锁(除了涉及到size计算等操作);在JDK 1.8中,读操作通过volatile关键字保证可见性,性能较高。
  • 高并发写
    • HashMap:非线程安全,高并发写时可能会出现数据覆盖等问题,性能也会因同步机制的添加而大幅下降。
    • ConcurrentHashMap:JDK 1.7通过分段锁,不同Segment的写操作可以并发执行;JDK 1.8采用CAS和synchronized,在写操作时竞争更小,性能优于HashMap。
  • 大量数据插入
    • HashMap:扩容时需要重新计算哈希和移动元素,在大量数据插入时,扩容开销较大,性能会受到影响。
    • ConcurrentHashMap:JDK 1.8中在大量数据插入时,由于采用多线程并行扩容,相比HashMap单线程扩容,性能更优。

4. 根据业务场景选择合适的集合类

  • 非并发场景:如果业务场景是非并发的,HashMap是一个很好的选择,因为它简单高效,没有线程安全带来的额外开销。
  • 高并发读多写少:ConcurrentHashMap在这种场景下表现出色,特别是JDK 1.8版本,读操作几乎无锁,性能很高。
  • 高并发写多读少:同样可以选择ConcurrentHashMap,JDK 1.8的优化使得写操作的竞争减小,性能较好。如果写操作频率极高且对数据一致性要求不是特别严格,还可以考虑使用CopyOnWriteArrayList(针对List场景)的思想来实现类似集合。
  • 对数据一致性要求极高:在高并发场景下,如果对数据一致性要求极高,如银行转账等场景,需要在使用ConcurrentHashMap时结合其他同步机制来保证操作的原子性和一致性。