forked from JavaDevTeam/notes
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathjava-util-map-HashMap.java
More file actions
56 lines (40 loc) · 1.47 KB
/
java-util-map-HashMap.java
File metadata and controls
56 lines (40 loc) · 1.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
--------------------
HashMap |
--------------------
# JDK 1.7及以前使用 Hash 表 + 链表实现
# JDK 1.8及以后使用 Hash 表 + 红黑树实现(优化了查询效率)
* 当触发了一定的条件后,会把链表转换为红黑树
# Hash表的尺寸和容量非常的重要
* 一般来说,Hash表这个容器当有数据要插入时,都会检查容量有没有超过设定的thredhold,如果超过,需要增大hash表的尺寸
* 但是这样一来,整个hash表里的无素都需要被重算一遍,这叫rehash,这个成本相当的大
# 核心的成员变量
static final int TREEIFY_THRESHOLD = 8;
* 用于判断是否需要将链表转换为红黑树的阈值
* 如果链表的长度大于了该值,大于了该值就会转换为红黑树
int size;
* 存储的数据数量
int modCount;
* 修改的次数
float loadFactor;
* 负载因子,决定了什么时候会触发扩容
容器大小 x 负载因子 = 触发扩容的大小
int threshold;
* 调整Map大小的下一个值
Node<K,V>[] table;
* 存放数据的数组
Set<Map.Entry<K,V>> entrySet;
# HashMap 在并发场景下使用时容易出现问题
* 多线程put操作后,get操作导致死循环(据说jdk8修复了这个问题)
HashMap<String, String> map = new HashMap<String, String>();
for (int i = 0; i < 1000; i++) {
new Thread(new Runnable() {
@Override
public void run() {
map.put(UUID.randomUUID().toString(), "");
}
}).start();
}
* HashMap 扩容的时候会调用 resize() 方法
* 这里的并发操作容易在一个桶上形成环形链表
* 当获取一个不存在的 key 时,计算出的 index 正好是环形链表的下标就会出现死循环
* 多线程put非null元素后,get操作得到null值(导致元素丢失,这个问题jdk8没有修复)