在Java面试的过程中经常会问到数据结构,而频率最高的就是 ConcurrentHashMap和HashMap
一、HashMap的数据结构与扩容机制
-
数据结构:HashMap内部主要由一个数组和链表(或在Java 8及以后可能是红黑树)组成。数组的每一个元素称为一个桶,每个桶存储一个链表,链表中的每个节点就是我们存入HashMap的一个键值对。当向HashMap添加元素时,会计算元素键的哈希值,然后与数组长度进行运算,确定元素在数组中的位置。如果该位置已有其他元素,新元素将被添加到该位置的链表中。
-
扩容机制:当HashMap中的元素数量达到一定阈值时,会触发扩容操作。扩容主要包括两步:增加数组的长度(通常是原长度的两倍);重新哈希,即因数组长度变化导致元素位置可能发生变化,需要重新计算每个元素的位置。扩容是一个耗时操作,因此,如果预知要存储的元素数量,最好在创建HashMap时就设置一个足够大的初始容量,避免后续的扩容操作。
1.7到1.8 ConcurrentHashMap的变动
-
锁分段与CAS:Java 7的ConcurrentHashMap使用了一种称为锁分段的技术,通过将数据分为若干段,每段数据有自己的锁,实现多线程同时写入不同段数据,从而提高并发性。然而,这种设计在某些情况下可能导致不必要的内容争用。相比之下,Java 8的ConcurrentHashMap不再使用锁分段,而是采用了CAS(Compare-and-Swap)方法,无需锁就能安全执行并发更新,性能得到提升。
-
数据结构:在Java 7中,ConcurrentHashMap内部使用了一个Segment数组,每个Segment包含一个HashEntry数组,每个HashEntry包含一个键值对。而在Java 8中,ConcurrentHashMap的数据结构更为简单,直接使用一个Node数组,每个Node包含一个键值对。当发生哈希冲突时,Node会形成链表或红黑树。
-
扩容机制:Java 7的ConcurrentHashMap在一个段中的元素数量超过阈值时,会锁定整个段并进行扩容。而Java 8的ConcurrentHashMap在扩容时粒度更细,当一个桶中的节点数量超过阈值时,只有这个桶会被扩容,这样可以减少锁的竞争,提高并发性。
-
函数式编程:Java 8引入了函数式编程的概念,ConcurrentHashMap也相应地添加了一些新的函数式编程方法,如forEach、reduce和computeIfAbsent等。
(个人理解,不正之处,还望指正 ^ ^)