Java集合框架之Map

Map是一种键值映射关系集合,Map集合中的键必须是唯一的不能重复的。Map包含很多的实现类,但在日常开发中常用的有HashMap,HashTable,ConcurrentHashMap。

一、HashMap

HashMap是一个允许存入空值空键、线程非安全的Map集合,它底层的数据结构是一个哈希表(数组+链表)。

java怎么设置map常量 java定义map常量_HashMap

HashMap添加元素:①计算元素key的hash值。②根据元素key的hash值对数组长度取余获取数组下标。③遍历数组下标对应的链表,通过equals方法判断元素key是否相等。④如果遍历过程中存在key相等,则进行value的替换并返回旧的value;如果该条链表中不存在要添加的key,则将要添加的元素添加到链表尾部并返回该元素中的value。

HashMap的node节点设计:是HashMap的一个静态内部类。

java怎么设置map常量 java定义map常量_集合_02

HashMap默认的数组长度是16,加载因子是0.75,扩容因子是2。当创建HashMap时指定了一个数组长度时,实际创建出来的HashMap数组长度为大于等于且距离指定值最近的一个2的次方数。如:指定值为6,则实际值为8;指定值为32,则实际值为32。

为了保证添加和读取元素的效率,链表不宜过长,所以当元素个数达到阈值后添加元素需要扩容。

阈值:16*0.75=12

当添加第13个元素时进行扩容,扩容后的数组大小:16*2=32

扩容:将原哈希表中的每个元素上述添加思想添加到新的哈希表中。

通过上面描述我们能得出一个结论:HashMap的数组长度时2的次方数。

那么为什么是2的次方数呢?原因在于JDK的开发者处于对HashMap的操作效率的考虑,在元素中key对数组长度取余计算数组下标时并没有采用取余(%)符号,而是采用了与(&)符号。

java怎么设置map常量 java定义map常量_ConcurrentHashMap_03

要想(length - 1)& hash == hash % length 则需要length为2的次方数。

二、ConcurrentHashMap

ConcurrentHashMap是一个不允许空键但允许空值、线程安全的Map集合。它底层的数据结构是一个哈希表数组,数组的每个元素都是一个哈希表。

java怎么设置map常量 java定义map常量_ConcurrentHashMap_04

ConcurrentHashMap使用锁分段技术实现线程安全,对桶数组的每个元素哈希表进行加锁,如果多个写操作分布在不同的桶中执行,那么它们是可以并发执行的。

ConcurrentHashMap桶数组长度为16,哈希表默认情况下与HashMap相同,扩容原理也一样,只是桶数组的大小保持不变。

ConcurrentHashMap的Node节点设计:它是ConcurrentHashMap的静态内部类。

java怎么设置map常量 java定义map常量_Map_05

ConcurrentHashMap添加元素:①计算元素key的hash值。②hash值对桶数组长度取余计算桶数组下标。③对该桶进行加锁。④对hash值对哈希表数组长度进行取余,和HashMap一样添加元素。⑤释放锁并返回旧值或新值。

ConcurrentHashMap获取元素:①计算元素key的hash值。②hash值对桶数组长度取余计算桶数组下标。③hash值对哈希表数组长度进行取余计算链表数组下标。④遍历链表,通过equals方法判断该元素是否存在。如果存在,则返回对应value;不存在,则返回null。

ConcurrentHashMap的get方法并没有加锁,原因在于①ConcurrentHashMap是基于happens-before先行原则,即写先于读。对同一元素的读和写操作,读会在写操作之后执行。②通过Node的设计,我们可以看出val是被volatile修饰的,即当val值发生改变时,线程会从主存中重新读取数据并写入当前线程的缓存中。