前言
Map 这样的 Key Value 在软件开发中是非常经典的结构,常用于在内存中存放数据。本文主要详解HashMap的结构和并发下HashMap的问题
说明
HashMap在并发环境下会有并发问题,所以在并发生产环境下请使用ConcurrentHashMap或者Collections.synchronizedMap(new HashMap()) 但是后一种方法性能太低,一般使用前者
HashMap
HashMap底层是采用数组+链表的数据结构,不过这种结构在JDK1.8中稍微有所不同,下面详细介绍:
Base1.7
实现
- 先看一下HashMap的成员变量
成员变量说明
①初始化桶大小,因为底层是数组,所以这是数组默认的大小。
②桶最大值。
③默认的负载因子(0.75)
④table 真正存放数据的数组。
⑤Map 存放数量的大小。
⑥桶大小,可在初始化时显式指定。
⑦负载因子,可在初始化时显式指定(由于给定的 HashMap 的容量大小是固定的,比如默认初始化, 给定的默认容量为 16,负载因子为 0.75。Map 在使用过程中不断的往里面存放数据,当数量达到了 16 * 0.75 = 12 就需要将当前 16 的容量进行扩容)
- 初始化
public HashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
threshold = initialCapacity;
init();
}
- Entry
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
Entry是存储的是数据基本单位, 是 HashMap 中的一个内部类,从他的成员变量很容易看出:
- key 就是写入时的键。
value 自然就是值。
next 开始的时候就提到 HashMap 是由数组和链表组成,所以这个就是用于实现链表结构。
hash 存放的是当前 key 的 hashcode。
- put
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))