-
Notifications
You must be signed in to change notification settings - Fork 615
/
Copy pathHashMap.java
2398 lines (2237 loc) · 89.6 KB
/
HashMap.java
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
package java.util;
import sun.misc.SharedSecrets;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* HashMap是常用的Java集合之一,是基于哈希表的Map接口的实现。与HashTable主要区别为不支持同步和允许null作为key和value。
* HashMap非线程安全,即任一时刻可以有多个线程同时写HashMap,可能会导致数据的不一致。
* 如果需要满足线程安全,可以用 Collections的synchronizedMap方法使HashMap具有线程安全的能力,或者使用ConcurrentHashMap。
* 在JDK1.6中,HashMap采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。
* 但是当位于一个数组中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。
* 而JDK1.8中,HashMap采用数组+链表+红黑树实现,当链表长度超过阈值8时,将链表转换为红黑树,这样大大减少了查找时间。
* 原本Map.Entry接口的实现类Entry改名为了Node。转化为红黑树时改用另一种实现TreeNode。
*/
public class HashMap<K, V> extends AbstractMap<K, V>
implements Map<K, V>, Cloneable, Serializable {
private static final long serialVersionUID = 362498820763181265L;
/**
* 默认的初始容量(容量为HashMap中槽的数目)是16,且实际容量必须是2的整数次幂。
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
/**
* 最大容量(必须是2的幂且小于2的30次方,传入容量过大将被这个值替换)
*/
static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* 默认装填因子0.75,如果当前键值对个数 >= HashMap最大容量*装填因子,进行rehash操作
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
* JDK1.8 新加,Entry链表最大长度,当桶中节点数目大于该长度时,将链表转成红黑树存储;
*/
static final int TREEIFY_THRESHOLD = 8;
/**
* JDK1.8 新加,当桶中节点数小于该长度,将红黑树转为链表存储;
*/
static final int UNTREEIFY_THRESHOLD = 6;
/**
* 桶可能被转化为树形结构的最小容量。当哈希表的大小超过这个阈值,才会把链式结构转化成树型结构,否则仅采取扩容来尝试减少冲突。
* 应该至少4*TREEIFY_THRESHOLD来避免扩容和树形结构化之间的冲突。
*/
static final int MIN_TREEIFY_CAPACITY = 64;
/**
* JDK1.6用Entry描述键值对,JDK1.8中用Node代替Entry
*/
static class Node<K, V> implements Map.Entry<K, V> {
// hash存储key的hashCode
final int hash;
// final:一个键值对的key不可改变
final K key;
V value;
//指向下个节点的引用
Node<K, V> next;
//构造函数
Node(int hash, K key, V value, Node<K, V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
public final K getKey() {
return key;
}
public final V getValue() {
return value;
}
public final String toString() {
return key + "=" + value;
}
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
public final boolean equals(Object o) {
if (o == this)
return true;
if (o instanceof Map.Entry) {
Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
if (Objects.equals(key, e.getKey()) &&
Objects.equals(value, e.getValue()))
return true;
}
return false;
}
}
/* ---------------- Static utilities -------------- */
/**
* HashMap中键值对的存储形式为链表节点,hashCode相同的节点(位于同一个桶)用链表组织
* hash方法分为三步:
* 1.取key的hashCode
* 2.key的hashCode高16位异或低16位
* 3.将第一步和第二步得到的结果进行取模运算。
*/
static final int hash(Object key) {
int h;
//计算key的hashCode, h = Objects.hashCode(key)
//h >>> 16表示对h无符号右移16位,高位补0,然后h与h >>> 16按位异或
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
/**
* 如果参数x实现了Comparable接口,返回参数x的类名,否则返回null
*/
static Class<?> comparableClassFor(Object x) {
if (x instanceof Comparable) {
Class<?> c;
Type[] ts, as;
Type t;
ParameterizedType p;
if ((c = x.getClass()) == String.class) // bypass checks
return c;
if ((ts = c.getGenericInterfaces()) != null) {
for (int i = 0; i < ts.length; ++i) {
if (((t = ts[i]) instanceof ParameterizedType) &&
((p = (ParameterizedType) t).getRawType() ==
Comparable.class) &&
(as = p.getActualTypeArguments()) != null &&
as.length == 1 && as[0] == c) // type arg is c
return c;
}
}
}
return null;
}
/**
* 如果x的类型为kc,则返回k.compareTo(x),否则返回0
*/
@SuppressWarnings({"rawtypes", "unchecked"}) // for cast to Comparable
static int compareComparables(Class<?> kc, Object k, Object x) {
return (x == null || x.getClass() != kc ? 0 :
((Comparable) k).compareTo(x));
}
/**
* 结果为>=cap的最小2的自然数幂
*/
static final int tableSizeFor(int cap) {
//先移位再或运算,最终保证返回值是2的整数幂
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
/* ---------------- Fields -------------- */
/**
* 哈希桶数组,分配的时候,table的长度总是2的幂
*/
transient Node<K, V>[] table;
/**
* HashMap将数据转换成set的另一种存储形式,这个变量主要用于迭代功能
*/
transient Set<Map.Entry<K, V>> entrySet;
/**
* 实际存储的数量,则HashMap的size()方法,实际返回的就是这个值,isEmpty()也是判断该值是否为0
*/
transient int size;
/**
* hashmap结构被改变的次数,fail-fast机制
*/
transient int modCount;
/**
* HashMap的扩容阈值,在HashMap中存储的Node键值对超过这个数量时,自动扩容容量为原来的二倍
*
* @serial
*/
int threshold;
/**
* HashMap的负加载因子,可计算出当前table长度下的扩容阈值:threshold = loadFactor * table.length
*
* @serial
*/
final float loadFactor;
/* ---------------- Public operations -------------- */
/**
* 使用指定的初始化容量initial capacity 和加载因子load factor构造一个空HashMap
*
* @param initialCapacity 初始化容量
* @param loadFactor 加载因子
* @throws IllegalArgumentException 如果指定的初始化容量为负数或者加载因子为非正数
*/
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;
this.threshold = tableSizeFor(initialCapacity);
}
/**
* 使用指定的初始化容量initial capacity和默认加载因子DEFAULT_LOAD_FACTOR(0.75)构造一个空HashMap
*
* @param initialCapacity 初始化容量
* @throws IllegalArgumentException 如果指定的初始化容量为负数
*/
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
/**
* 使用指定的初始化容量(16)和默认加载因子DEFAULT_LOAD_FACTOR(0.75)构造一个空HashMap
*/
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
/**
* 使用指定Map m构造新的HashMap。使用指定的初始化容量(16)和默认加载因子DEFAULT_LOAD_FACTOR(0.75)
*
* @param m 指定的map
* @throws NullPointerException 如果指定的map是null
*/
public HashMap(Map<? extends K, ? extends V> m) {
this.loadFactor = DEFAULT_LOAD_FACTOR;
putMapEntries(m, false);
}
/**
* Map.putAll and Map constructor的实现需要的方法
* 将m的键值对插入本map中
*
* @param m 指定的map
* @param evict 初始化map时使用false,否则使用true
*/
final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
int s = m.size();
//如果参数map不为空
if (s > 0) {
// 判断table是否已经初始化
if (table == null) { // pre-size
// 未初始化,s为m的实际元素个数
float ft = ((float) s / loadFactor) + 1.0F;
int t = ((ft < (float) MAXIMUM_CAPACITY) ?
(int) ft : MAXIMUM_CAPACITY);
// 计算得到的t大于阈值,则初始化阈值
if (t > threshold)
//根据容量初始化临界值
threshold = tableSizeFor(t);
// 已初始化,并且m元素个数大于阈值,进行扩容处理
} else if (s > threshold)
//扩容处理
resize();
// 将m中的所有元素添加至HashMap中
for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
K key = e.getKey();
V value = e.getValue();
putVal(hash(key), key, value, false, evict);
}
}
}
/**
* 返回map中键值对映射的个数
*
* @return map中键值对映射的个数
*/
public int size() {
return size;
}
/**
* 如果map中没有键值对映射,返回true
*
* @return 如果map中没有键值对映射,返回true
*/
public boolean isEmpty() {
return size == 0;
}
/**
* 返回指定的key映射的value,如果value为null,则返回null
* get可以分为三个步骤:
* 1.通过hash(Object key)方法计算key的哈希值hash。
* 2.通过getNode( int hash, Object key)方法获取node。
* 3.如果node为null,返回null,否则返回node.value。
*
* @see #put(Object, Object)
*/
public V get(Object key) {
Node<K, V> e;
//根据key及其hash值查询node节点,如果存在,则返回该节点的value值
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
/**
* 根据key的哈希值和key获取对应的节点
* getNode可分为以下几个步骤:
* 1.如果哈希表为空,或key对应的桶为空,返回null
* 2.如果桶中的第一个节点就和指定参数hash和key匹配上了,返回这个节点。
* 3.如果桶中的第一个节点没有匹配上,而且有后续节点
* 3.1如果当前的桶采用红黑树,则调用红黑树的get方法去获取节点
* 3.2如果当前的桶不采用红黑树,即桶中节点结构为链式结构,遍历链表,直到key匹配
* 4.找到节点返回null,否则返回null。
*
* @param hash 指定参数key的哈希值
* @param key 指定参数key
* @return 返回node,如果没有则返回null
*/
final Node<K, V> getNode(int hash, Object key) {
Node<K, V>[] tab;
Node<K, V> first, e;
int n;
K k;
//如果哈希表不为空,而且key对应的桶上不为空
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
//如果桶中的第一个节点就和指定参数hash和key匹配上了
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
//返回桶中的第一个节点
return first;
//如果桶中的第一个节点没有匹配上,而且有后续节点
if ((e = first.next) != null) {
//如果当前的桶采用红黑树,则调用红黑树的get方法去获取节点
if (first instanceof TreeNode)
return ((TreeNode<K, V>) first).getTreeNode(hash, key);
//如果当前的桶不采用红黑树,即桶中节点结构为链式结构
do {
//遍历链表,直到key匹配
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
//如果哈希表为空,或者没有找到节点,返回null
return null;
}
/**
* 如果map中含有key为指定参数key的键值对,返回true
*
* @param key 指定参数key
* @return 如果map中含有key为指定参数key的键值对,返回true
* key.
*/
public boolean containsKey(Object key) {
return getNode(hash(key), key) != null;
}
/**
* 将指定参数key和指定参数value插入map中,如果key已经存在,那就替换key对应的value
* put(K key, V value)可以分为三个步骤:
* 1.通过hash(Object key)方法计算key的哈希值。
* 2.通过putVal(hash(key), key, value, false, true)方法实现功能。
* 3.返回putVal方法返回的结果。
*
* @param key 指定key
* @param value 指定value
* @return 如果value被替换,则返回旧的value,否则返回null。当然,可能key对应的value就是null
*/
public V put(K key, V value) {
// 倒数第二个参数false:表示允许旧值替换
// 最后一个参数true:表示HashMap不处于创建模式
return putVal(hash(key), key, value, false, true);
}
/**
* Map.put和其他相关方法的实现需要的方法
* putVal方法可以分为下面的几个步骤:
* 1.如果哈希表为空,调用resize()创建一个哈希表。
* 2.如果指定参数hash在表中没有对应的桶,即为没有碰撞,直接将键值对插入到哈希表中即可。
* 3.如果有碰撞,遍历桶,找到key映射的节点
* 3.1桶中的第一个节点就匹配了,将桶中的第一个节点记录起来。
* 3.2如果桶中的第一个节点没有匹配,且桶中结构为红黑树,则调用红黑树对应的方法插入键值对。
* 3.3如果不是红黑树,那么就肯定是链表。遍历链表,如果找到了key映射的节点,就记录这个节点,退出循环。如果没有找到,在链表尾部插入节点。插入后,如果链的长度大于等于TREEIFY_THRESHOLD这个临界值,则使用treeifyBin方法把链表转为红黑树。
* 4.如果找到了key映射的节点,且节点不为null
* 4.1记录节点的vlaue。
* 4.2如果参数onlyIfAbsent为false,或者oldValue为null,替换value,否则不替换。
* 4.3返回记录下来的节点的value。
* 5.如果没有找到key映射的节点(2、3步中讲了,这种情况会插入到hashMap中),插入节点后size会加1,这时要检查size是否大于临界值threshold,如果大于会使用resize方法进行扩容。
*
* @param hash 指定参数key的哈希值
* @param key 指定参数key
* @param value 指定参数value
* @param onlyIfAbsent 如果为true,即使指定参数key在map中已经存在,也不会替换value
* @param evict 如果为false,数组table在创建模式中
* @return 如果value被替换,则返回旧的value,否则返回null。当然,可能key对应的value就是null。
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K, V>[] tab;
Node<K, V> p;
int n, i;
//如果哈希表为空,调用resize()创建一个哈希表,并用变量n记录哈希表长度
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
/**
* 如果指定参数hash在表中没有对应的桶,即为没有碰撞
* Hash函数,(n - 1) & hash 计算key将被放置的槽位
* (n - 1) & hash 本质上是hash % n,位运算更快
*/
if ((p = tab[i = (n - 1) & hash]) == null)
//直接将键值对插入到map中即可
tab[i] = newNode(hash, key, value, null);
else {// 桶中已经存在元素
Node<K, V> e;
K k;
// 比较桶中第一个元素(数组中的结点)的hash值相等,key相等
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
// 将第一个元素赋值给e,用e来记录
e = p;
// 当前桶中无该键值对,且桶是红黑树结构,按照红黑树结构插入
else if (p instanceof TreeNode)
e = ((TreeNode<K, V>) p).putTreeVal(this, tab, hash, key, value);
// 当前桶中无该键值对,且桶是链表结构,按照链表结构插入到尾部
else {
for (int binCount = 0; ; ++binCount) {
// 遍历到链表尾部
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
// 检查链表长度是否达到阈值,达到将该槽位节点组织形式转为红黑树
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
// 链表节点的<key, value>与put操作<key, value>相同时,不做重复操作,跳出循环
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
// 找到或新建一个key和hashCode与插入元素相等的键值对,进行put操作
if (e != null) { // existing mapping for key
// 记录e的value
V oldValue = e.value;
/**
* onlyIfAbsent为false或旧值为null时,允许替换旧值
* 否则无需替换
*/
if (!onlyIfAbsent || oldValue == null)
e.value = value;
// 访问后回调
afterNodeAccess(e);
// 返回旧值
return oldValue;
}
}
// 更新结构化修改信息
++modCount;
// 键值对数目超过阈值时,进行rehash
if (++size > threshold)
resize();
// 插入后回调
afterNodeInsertion(evict);
return null;
}
/**
* 对table进行初始化或者扩容。
* 如果table为null,则对table进行初始化
* 如果对table扩容,因为每次扩容都是翻倍,与原来计算(n-1)&hash的结果相比,节点要么就在原来的位置,要么就被分配到“原位置+旧容量”这个位置
* resize的步骤总结为:
* 1.计算扩容后的容量,临界值。
* 2.将hashMap的临界值修改为扩容后的临界值
* 3.根据扩容后的容量新建数组,然后将hashMap的table的引用指向新数组。
* 4.将旧数组的元素复制到table中。
*
* @return the table
*/
final Node<K, V>[] resize() {
//新建oldTab数组保存扩容前的数组table
Node<K, V>[] oldTab = table;
//获取原来数组的长度
int oldCap = (oldTab == null) ? 0 : oldTab.length;
//原来数组扩容的临界值
int oldThr = threshold;
int newCap, newThr = 0;
//如果扩容前的容量 > 0
if (oldCap > 0) {
//如果原来的数组长度大于最大值(2^30)
if (oldCap >= MAXIMUM_CAPACITY) {
//扩容临界值提高到正无穷
threshold = Integer.MAX_VALUE;
//无法进行扩容,返回原来的数组
return oldTab;
//如果现在容量的两倍小于MAXIMUM_CAPACITY且现在的容量大于DEFAULT_INITIAL_CAPACITY
} else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
//临界值变为原来的2倍
newThr = oldThr << 1;
} else if (oldThr > 0) //如果旧容量 <= 0,而且旧临界值 > 0
//数组的新容量设置为老数组扩容的临界值
newCap = oldThr;
else { //如果旧容量 <= 0,且旧临界值 <= 0,新容量扩充为默认初始化容量,新临界值为DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY
newCap = DEFAULT_INITIAL_CAPACITY;//新数组初始容量设置为默认值
newThr = (int) (DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);//计算默认容量下的阈值
}
// 计算新的resize上限
if (newThr == 0) {//在当上面的条件判断中,只有oldThr > 0成立时,newThr == 0
//ft为临时临界值,下面会确定这个临界值是否合法,如果合法,那就是真正的临界值
float ft = (float) newCap * loadFactor;
//当新容量< MAXIMUM_CAPACITY且ft < (float)MAXIMUM_CAPACITY,新的临界值为ft,否则为Integer.MAX_VALUE
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float) MAXIMUM_CAPACITY ?
(int) ft : Integer.MAX_VALUE);
}
//将扩容后hashMap的临界值设置为newThr
threshold = newThr;
//创建新的table,初始化容量为newCap
@SuppressWarnings({"rawtypes", "unchecked"})
Node<K, V>[] newTab = (Node<K, V>[]) new Node[newCap];
//修改hashMap的table为新建的newTab
table = newTab;
//如果旧table不为空,将旧table中的元素复制到新的table中
if (oldTab != null) {
//遍历旧哈希表的每个桶,将旧哈希表中的桶复制到新的哈希表中
for (int j = 0; j < oldCap; ++j) {
Node<K, V> e;
//如果旧桶不为null,使用e记录旧桶
if ((e = oldTab[j]) != null) {
//将旧桶置为null
oldTab[j] = null;
//如果旧桶中只有一个node
if (e.next == null)
//将e也就是oldTab[j]放入newTab中e.hash & (newCap - 1)的位置
newTab[e.hash & (newCap - 1)] = e;
//如果旧桶中的结构为红黑树
else if (e instanceof TreeNode)
//将树中的node分离
((TreeNode<K, V>) e).split(this, newTab, j, oldCap);
else { //如果旧桶中的结构为链表,链表重排,jdk1.8做的一系列优化
Node<K, V> loHead = null, loTail = null;
Node<K, V> hiHead = null, hiTail = null;
Node<K, V> next;
//遍历整个链表中的节点
do {
next = e.next;
// 原索引
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
} else {// 原索引+oldCap
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
// 原索引放到bucket里
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
// 原索引+oldCap放到bucket里
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
/**
* 将链表转化为红黑树
*/
final void treeifyBin(Node<K, V>[] tab, int hash) {
int n, index;
Node<K, V> e;
//如果桶数组table为空,或者桶数组table的长度小于MIN_TREEIFY_CAPACITY,不符合转化为红黑树的条件
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
//扩容
resize();
//如果符合转化为红黑树的条件,而且hash对应的桶不为null
else if ((e = tab[index = (n - 1) & hash]) != null) {
// 红黑树的头、尾节点
TreeNode<K, V> hd = null, tl = null;
//遍历链表
do {
//替换链表node为树node,建立双向链表
TreeNode<K, V> p = replacementTreeNode(e, null);
// 确定树头节点
if (tl == null)
hd = p;
else {
p.prev = tl;
tl.next = p;
}
tl = p;
} while ((e = e.next) != null);
//遍历链表插入每个节点到红黑树
if ((tab[index] = hd) != null)
hd.treeify(tab);
}
}
/**
* 将参数map中的所有键值对映射插入到hashMap中,如果有碰撞,则覆盖value。
*
* @param m 参数map
* @throws NullPointerException 如果map为null
*/
public void putAll(Map<? extends K, ? extends V> m) {
putMapEntries(m, true);
}
/**
* 删除hashMap中key映射的node
* remove方法的实现可以分为三个步骤:
* 1.通过 hash(Object key)方法计算key的哈希值。
* 2.通过 removeNode 方法实现功能。
* 3.返回被删除的node的value。
*
* @param key 参数key
* @return 如果没有映射到node,返回null,否则返回对应的value
*/
public V remove(Object key) {
Node<K, V> e;
//根据key来删除node。removeNode方法的具体实现在下面
return (e = removeNode(hash(key), key, null, false, true)) == null ?
null : e.value;
}
/**
* Map.remove和相关方法的实现需要的方法
* removeNode方法的步骤总结为:
* 1.如果数组table为空或key映射到的桶为空,返回null。
* 2.如果key映射到的桶上第一个node的就是要删除的node,记录下来。
* 3.如果桶内不止一个node,且桶内的结构为红黑树,记录key映射到的node。
* 4.桶内的结构不为红黑树,那么桶内的结构就肯定为链表,遍历链表,找到key映射到的node,记录下来。
* 5.如果被记录下来的node不为null,删除node,size-1被删除。
* 6.返回被删除的node。
*
* @param hash key的哈希值
* @param key key的哈希值
* @param value 如果 matchValue 为true,则value也作为确定被删除的node的条件之一,否则忽略
* @param matchValue 如果为true,则value也作为确定被删除的node的条件之一
* @param movable 如果为false,删除node时不会删除其他node
* @return 返回被删除的node,如果没有node被删除,则返回null(针对红黑树的删除方法)
*/
final Node<K, V> removeNode(int hash, Object key, Object value,
boolean matchValue, boolean movable) {
Node<K, V>[] tab;
Node<K, V> p;
int n, index;
//如果数组table不为空且key映射到的桶不为空
if ((tab = table) != null && (n = tab.length) > 0 &&
(p = tab[index = (n - 1) & hash]) != null) {
Node<K, V> node = null, e;
K k;
V v;
//如果桶上第一个node的就是要删除的node
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
//记录桶上第一个node
node = p;
else if ((e = p.next) != null) {//如果桶内不止一个node
//如果桶内的结构为红黑树
if (p instanceof TreeNode)
//记录key映射到的node
node = ((TreeNode<K, V>) p).getTreeNode(hash, key);
else {//如果桶内的结构为链表
do {//遍历链表,找到key映射到的node
if (e.hash == hash &&
((k = e.key) == key ||
(key != null && key.equals(k)))) {
//记录key映射到的node
node = e;
break;
}
p = e;
} while ((e = e.next) != null);
}
}
//如果得到的node不为null且(matchValue为false||node.value和参数value匹配)
if (node != null && (!matchValue || (v = node.value) == value ||
(value != null && value.equals(v)))) {
//如果桶内的结构为红黑树
if (node instanceof TreeNode)
//使用红黑树的删除方法删除node
((TreeNode<K, V>) node).removeTreeNode(this, tab, movable);
else if (node == p)//如果桶的第一个node的就是要删除的node
//删除node
tab[index] = node.next;
else//如果桶内的结构为链表,使用链表删除元素的方式删除node
p.next = node.next;
++modCount;//结构性修改次数+1
--size;//哈希表大小-1
afterNodeRemoval(node);
return node;//返回被删除的node
}
}
return null;//如果数组table为空或key映射到的桶为空,返回null。
}
/**
* 删除map中所有的键值对
*/
public void clear() {
Node<K, V>[] tab;
modCount++;
if ((tab = table) != null && size > 0) {
size = 0;
for (int i = 0; i < tab.length; ++i)
tab[i] = null;
}
}
/**
* 如果hashMap中的键值对有一对或多对的value为参数value,返回true
*
* @param value 参数value
* @return 如果hashMap中的键值对有一对或多对的value为参数value,返回true
*/
public boolean containsValue(Object value) {
Node<K, V>[] tab;
V v;
if ((tab = table) != null && size > 0) {
//遍历数组table
for (int i = 0; i < tab.length; ++i) {
//遍历桶中的node
for (Node<K, V> e = tab[i]; e != null; e = e.next) {
if ((v = e.value) == value ||
(value != null && value.equals(v)))
return true;
}
}
}
return false;
}
/**
* 返回hashMap中所有key的视图。
* 改变hashMap会影响到set,反之亦然。
* 如果当迭代器迭代set时,hashMap被修改(除非是迭代器自己的remove()方法),迭代器的结果是不确定的。
* set支持元素的删除,通过Iterator.remove、Set.remove、removeAll、retainAll、clear操作删除hashMap中对应的键值对。
* 不支持add和addAll方法。
*
* @return 返回hashMap中所有key的set视图
*/
public Set<K> keySet() {
Set<K> ks = keySet;
if (ks == null) {
ks = new KeySet();
keySet = ks;
}
return ks;
}
/**
* 内部类KeySet
*/
final class KeySet extends AbstractSet<K> {
public final int size() {
return size;
}
public final void clear() {
HashMap.this.clear();
}
public final Iterator<K> iterator() {
return new KeyIterator();
}
public final boolean contains(Object o) {
return containsKey(o);
}
public final boolean remove(Object key) {
return removeNode(hash(key), key, null, false, true) != null;
}
public final Spliterator<K> spliterator() {
return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
}
public final void forEach(Consumer<? super K> action) {
Node<K, V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (Node<K, V> e = tab[i]; e != null; e = e.next)
action.accept(e.key);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
/**
* 返回hashMap中所有value的collection视图
* 改变hashMap会改变collection,反之亦然。
* 如果当迭代器迭代collection时,hashMap被修改(除非是迭代器自己的remove()方法),迭代器的结果是不确定的。
* collection支持元素的删除,通过Iterator.remove、Collection.remove、removeAll、retainAll、clear操作删除hashMap中对应的键值对。
* 不支持add和addAll方法。
*
* @return 返回hashMap中所有key的collection视图
*/
public Collection<V> values() {
Collection<V> vs = values;
if (vs == null) {
vs = new Values();
values = vs;
}
return vs;
}
/**
* 内部类Values
*/
final class Values extends AbstractCollection<V> {
public final int size() {
return size;
}
public final void clear() {
HashMap.this.clear();
}
public final Iterator<V> iterator() {
return new ValueIterator();
}
public final boolean contains(Object o) {
return containsValue(o);
}
public final Spliterator<V> spliterator() {
return new ValueSpliterator<>(HashMap.this, 0, -1, 0, 0);
}
public final void forEach(Consumer<? super V> action) {
Node<K, V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (Node<K, V> e = tab[i]; e != null; e = e.next)
action.accept(e.value);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
/**
* 返回hashMap中所有键值对的set视图
* 改变hashMap会影响到set,反之亦然。
* 如果当迭代器迭代set时,hashMap被修改(除非是迭代器自己的remove()方法),迭代器的结果是不确定的。
* set支持元素的删除,通过Iterator.remove、Set.remove、removeAll、retainAll、clear操作删除hashMap中对应的键值对。
* 不支持add和addAll方法。
*
* @return 返回hashMap中所有键值对的set视图
*/
public Set<Map.Entry<K, V>> entrySet() {
Set<Map.Entry<K, V>> es;
return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}
/**
* 内部类EntrySet
*/
final class EntrySet extends AbstractSet<Map.Entry<K, V>> {
public final int size() {
return size;
}
public final void clear() {
HashMap.this.clear();
}
public final Iterator<Map.Entry<K, V>> iterator() {
return new EntryIterator();
}
public final boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
Object key = e.getKey();
Node<K, V> candidate = getNode(hash(key), key);
return candidate != null && candidate.equals(e);
}
public final boolean remove(Object o) {
if (o instanceof Map.Entry) {
Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
Object key = e.getKey();
Object value = e.getValue();
return removeNode(hash(key), key, value, true, true) != null;
}
return false;
}
public final Spliterator<Map.Entry<K, V>> spliterator() {
return new EntrySpliterator<>(HashMap.this, 0, -1, 0, 0);
}
public final void forEach(Consumer<? super Map.Entry<K, V>> action) {
Node<K, V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (Node<K, V> e = tab[i]; e != null; e = e.next)
action.accept(e);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
// JDK8重写的方法
/**
* 通过key映射到对应node,如果没映射到则返回默认值defaultValue
*
* @param key
* @param defaultValue
* @return key映射到对应的node,如果没映射到则返回默认值defaultValue
*/
@Override
public V getOrDefault(Object key, V defaultValue) {
Node<K, V> e;
return (e = getNode(hash(key), key)) == null ? defaultValue : e.value;
}
/**
* 在hashMap中插入参数key和value组成的键值对,如果key在hashMap中已经存在,不替换value
*
* @param key
* @param value
* @return 如果key在hashMap中不存在,返回旧value
*/
@Override
public V putIfAbsent(K key, V value) {
return putVal(hash(key), key, value, true, true);
}
/**
* 删除hashMap中key为参数key,value为参数value的键值对。如果桶中结构为树,则级联删除
*
* @param key
* @param value
* @return 删除成功,返回true
*/