* j+ P2 \4 W: H3 R X: p# i源码分析重要属性 0 b$ q% e4 P, ]//默认初始容量是16 3 i _9 K0 q+ l( b7 b static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16$ ?5 U3 B+ d2 F8 Z2 h) u
! D4 y! D' j6 P" d+ _$ s: _
//最大容量是2的30次方 % i- }4 ]0 d. v5 V7 W7 n' X! J static final int MAXIMUM_CAPACITY = 1 << 30;) D, ^8 Q: @5 Q0 {. s8 }% l* }
# Q! w4 q& w; n' V: R0 a //当构造方法中没有指定负载因子时用这个默认的 : ~" v/ w9 @0 f3 e: l( b+ S static final float DEFAULT_LOAD_FACTOR = 0.75f; / p! R# V: m3 i) V& s8 F% {& J! T0 U$ a$ Q& }
//当链表中桶的个数大于等于8就会变成红黑树( O- o& s% e( _: L Q/ f6 b
static final int TREEIFY_THRESHOLD = 8; 8 P& W: F% x% D. H% l5 P! R, q
//当红黑树大小小于等于6时红黑树会转化成链表 / Y; |: U; M# u& q( ` static final int UNTREEIFY_THRESHOLD = 6;. B# [! Q/ w: T2 u! m, a+ o0 t
4 R4 f5 C2 }3 P
//当数组容量大于64时链表才能转成红黑树 0 W& M( y4 b6 p. h+ j8 Y static final int MIN_TREEIFY_CAPACITY = 64; / z& j, h# ~: K( ^ ) }6 s" V6 X5 [. T# f //数组,数组的大小是2的n次幂. l4 d9 N& R# x8 I, F
transient Node<K,V>[] table; , m7 J3 U( f6 S; P2 r0 o/ D' f+ v //存放具体集合 V3 ], X% O) S& |
transient Set<Map.Entry<K,V>> entrySet; a: J. G( Z/ Q) w6 r7 j" }) b7 ~ a5 x
//存放元素的个数0 x7 c: V+ `. X0 Z" [
transient int size; & b( B/ d: N3 a( U- q$ @6 z . W+ R/ I& H: V( ? //每次更改结构的计数器 ( \/ a: R/ g3 t4 h0 j7 Y0 l transient int modCount; ; D0 R' J! C2 x! p! }. I5 @6 w " j* r- q& U3 X3 ?5 I //当HashMap所能容纳键值对数量的最大值,超过这个值,需要扩容( }( ]0 X2 q3 t# L4 t8 V
int threshold;5 S! O! `. r. q' a$ F& S
6 U% B$ t9 x. Z //负载因子2 d! X* f% d n3 x; Z
final float loadFactor;! z6 q& G8 ^* q! S2 _3 {& _
loadFactory加载因子 . a ^- F' [# ]. o0 @, j, GloadFactory加载因子是通知数组存放数据的疏密程度,他的值越接近于1,存放在数组到的数据也就越密集,也就是说这个值越大,链表的长度会增加的越快。当他的值趋近于0,存放在entry中的数据就会越少,也就越稀疏。7 _9 X5 q% o8 W2 Z$ f
/ z) r9 U7 ]# F; N" M9 Z- @0 Athreshold ; F3 s9 c4 C" ?' j2 W给定的默认容量大小是16,负载因子时0.75,Map在使用过程中不断的往里面存放数据,当存放的桶的个数达到了12=16*0.75,就会扩容,扩容的时候比较消耗性能。. _- M4 A( e' z0 o7 L. h
( j* Z9 j1 x2 o) J" x* q; p% ~+ x5 \
你也许有疑问,12是怎算出来的,其实这个就是threshold属性,threshold=capacity*loadFactoy,当Size大于等于threshold这个值,就需要对数组进行扩容操作了。 $ |( V, l o. S* H ' }4 l: y4 g/ A$ N从上面的源码我们可以知道,容量最大是2的30次方,当数组大小 大于等于64并且链表长度大于等于8时,链表就会转化成红黑树。当红黑树的桶的个数 小于等于6时就会变回链表。 . q& w& p% z$ [" P1 i2 H3 k: Q% N6 D2 ~ ~' e
链表Node节点; d. A& J0 S( s# a" z* I7 i
& r9 D2 R' S- C0 p3 B//Node 节点实现了Map.Entry<K,V> ' H5 W1 S: I# }. F! d4 w+ O* Astatic class Node<K,V> implements Map.Entry<K,V> {" n/ g( h' r; Y& E0 B
final int hash; //hashCod ( w. K U# l8 O( D D- p1 ` final K key;//键8 C. u( N2 J4 d0 r6 e
V value;//值 0 {1 ?" }' N- E2 w) o# c' s( P) q Node<K,V> next;//指向下一个节点0 \- Z8 a! r! h1 F6 }8 g O J
//构造函数 0 j+ s$ V# ~4 B, X$ Z Node(int hash, K key, V value, Node<K,V> next) { 8 N5 R+ l% v1 X2 l( ] this.hash = hash;4 A0 m. n# |( T7 y0 I
this.key = key; 4 P0 N0 @7 h1 V. {5 M) G9 v this.value = value; ! }; }3 g% e0 b' u$ R this.next = next;9 [7 i, w0 i; e; b
} : f* l; y5 T" q3 F n$ e/ B7 U& K: ?5 @% V; o" m public final K getKey() { return key; }! W/ k+ Y* u. A V+ p9 Q' j5 V4 |$ M
public final V getValue() { return value; } 1 k2 T- j" i3 v/ }8 V9 | //重写toString方法2 U- s) a' {: C6 v2 Q2 h; Q( w" n
public final String toString() { return key + "=" + value; }1 N7 D/ o3 `& G. Q* J8 c0 i
//重写hashCode方法# L J' o; r! ~* \
public final int hashCode() {2 o- a$ [; K% x1 i5 K* h
return Objects.hashCode(key) ^ Objects.hashCode(value); y* R2 |1 z. i! {
}3 \3 I/ r1 a0 Q( Y0 U
. F7 N5 \* d" D$ [ }
public final V setValue(V newValue) {$ O+ x8 S6 n' }: r$ i
V oldValue = value;" j |4 h# \1 n6 H+ _1 q
value = newValue; ( v3 Q8 R/ ]1 y) L5 E return oldValue;1 H k G2 e9 G6 R/ Z, S
}. i, o" y$ }# v. Q- }- @/ c
//重写equals方法 : I1 U+ ^+ ]$ O! H& [! a. h1 K0 ]' l public final boolean equals(Object o) {, E! ^* v6 T3 T+ v. b$ g. F
//如果内存地址一致直接返回true 3 L' j4 h, t4 N8 n% N! q) b6 l$ | if (o == this)* e9 {2 k4 J: n) w, O
return true; : G+ h4 U: _4 W2 V7 e, b //比较的节点必须实现这个Map.Entry 1 L" m- N$ q2 j5 G/ T if (o instanceof Map.Entry) { " j& O) x t. |% R Map.Entry<?,?> e = (Map.Entry<?,?>)o; & X# s* m0 P# z+ J. ~ if (Objects.equals(key, e.getKey()) &&0 b7 v/ ?: Y9 @% i6 ^3 E
Objects.equals(value, e.getValue()))6 Q8 ]8 @0 g& o0 Q
//键和值都相等才能返回true 0 b6 r( d- o5 @ return true;! o6 \- A y+ f1 k7 m( [
}9 h. s9 N* ~8 ?9 z8 e0 Q) A6 Y( g9 J
return false;2 g. i: m( `- I/ ~" S/ P* T/ K5 x4 F
} ' A7 r* `5 a0 P& p* g5 V+ ^ }' H" x( w) F; D: [7 b
# N6 \) \1 L7 P* n, y) k# ]7 Y! A- }. z0 L% W b% X' q/ v; y 红黑树节点结构 9 Z S/ C# ?% u//TreeNode继承了LinkedHashMap中的Entry节点 . E* y+ x+ h' P, t( N: r/ xstatic final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> { / {8 K, v/ n6 n0 r) E TreeNode<K,V> parent; // 红黑树的父亲节点 . j' q4 b. b& c) Y5 c( y- z TreeNode<K,V> left; //左子树# q& X& D7 Q2 X
TreeNode<K,V> right;//右子树 ; `6 L( l- R/ F7 W# Q TreeNode<K,V> prev; // 链表中的节点,在删除元素的时候可以快速找到他的前驱节点 4 B/ o% f5 Y4 {) v& h boolean red; //是否变色, , T$ q7 ]6 c9 E6 h //构造方法 s8 f i& m1 o0 g! i7 p, R
TreeNode(int hash, K key, V val, Node<K,V> next) {% G) F% ~* g* a8 Y, O$ \
super(hash, key, val, next); ) n& e* G- d2 {4 X }( H9 m @- h* |: G9 t
7 f- y. x; E" y# A f* Z( E0 T
/**4 A: d% B# N, a3 j8 W
* 返回根节点 6 h6 z- k0 O, K% q) N+ O */# V( [2 \# p6 ?8 ]
final TreeNode<K,V> root() { & Q, G# A" q9 x4 C* G0 L! H for (TreeNode<K,V> r = this, p;;) {* E! `/ @: H; U+ N M" E
if ((p = r.parent) == null)0 W4 ~' o' t2 B6 i
return r; * u7 e) V/ x/ j4 V% w: `& @ r = p;' ?- s3 ]9 R$ d* R! v
} , v/ G" W" q$ a4 _& ~2 F }0 p! ?2 b+ b% w9 S
% L7 Z: w; x, {% J 构造方法/**9 p4 k- h; w: h$ s0 w
* 构造一个空的HashMap并指定初始容量和负载因子。 ! A. ~) z5 U2 a+ r& M3 f3 k5 `' z* % f& I2 Q) C5 C% |6 n. J w**/7 t. G1 m! v+ X3 c8 F5 I
public HashMap(int initialCapacity, float loadFactor) {4 H0 k( V) f, b6 T
//如果初始容量小于0,抛出非法参数异常 & _3 w j" E. E, t) Q" k. H if (initialCapacity < 0)5 B& ?4 p( d' s5 m; V
throw new IllegalArgumentException("Illegal initial capacity: " +) e7 m9 ]4 Y! U5 D$ v6 y8 N
initialCapacity);7 k% ^ s$ Q1 A: b( w. h* E& a
//如果初始容量大于最大的容量也就是2^30,那么就按照最大的初始容量赋值。 o6 q }9 ?: L* Q* ^* ^$ A3 W, K
if (initialCapacity > MAXIMUM_CAPACITY) $ }, m7 u3 t, L( C8 \ { initialCapacity = MAXIMUM_CAPACITY; 0 ?# W7 b+ e, n# H3 b //如果负载因子小于0或者是NaN(float NaN = 0.0f / 0.0f;)也会抛出非法参数异常 ' }& y( Y8 |. y. k) T# [ if (loadFactor <= 0 || Float.isNaN(loadFactor)) # |3 a& w( I; F- \# }( q) o0 k throw new IllegalArgumentException("Illegal load factor: " +. q, h, P4 ^/ ]4 o0 e: Z
loadFactor);. X2 i, K) |3 @4 T ]" \0 b
this.loadFactor = loadFactor; 6 l7 y: D# O% X3 @' J. u9 |' A this.threshold = tableSizeFor(initialCapacity); 1 C6 k$ e9 s o+ B }% W! S9 M4 s, G+ M$ M/ n
//如果只是指定了初始容量那么负载因子就是默认的0.750 Q+ |% n. Q: A$ n3 t
public HashMap(int initialCapacity) { $ \# Y# k5 Z' m! U this(initialCapacity, DEFAULT_LOAD_FACTOR); 6 s5 Q4 R5 e) {( E# x1 \. {" d } 3 }3 R# k* R1 z) X; \ G8 D$ ~( W% H. i' h# c4 s7 u
/** & q4 k0 V* y; g: |0 p * 如果什么都没指定,也就是无参构造,则初始容量是16和负载因子是0.75' s1 `/ M% R6 I/ Q: S& X
*/ / W0 s ]2 y8 f% b, }8 O/ f% h public HashMap() { " p- i1 k& W/ ]9 G$ o% r- m this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted& Z$ N l- \: z7 B! F
}2 X) x- ^% W2 X' |
% J8 W, z- @5 p, K" Y1 h /**- G' D3 p' C/ K
* 包含另一个Map的映射,如果被映射的Map是一个null会抛出空指针异常 ( z& r9 ~0 w- g; _ w9 i2 P- ~% \ * 负载因子时默认的% K6 S3 e, ]6 ^2 |6 L+ H7 m
*// u" |9 ]1 }7 _0 }# ^. @
public HashMap(Map<? extends K, ? extends V> m) {# F" L8 X) a; ^" B
this.loadFactor = DEFAULT_LOAD_FACTOR;+ u1 L1 g- w2 o/ b
putMapEntries(m, false);1 S% j& h7 L8 j- W9 o3 [
}6 w; d8 r" W# Z, x6 r$ r
; ], l1 s, e- S! E; J! Ufinal void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {8 ], {) _& h6 c c1 y Q
int s = m.size();//s是map的大小 ! X. l- ]7 [7 G6 ` if (s > 0) { * C; r7 A! [ o8 b' z, d f if (table == null) { // pre-size 7 q$ P8 w, S# A" u6 \4 E/ X float ft = ((float)s / loadFactor) + 1.0F; . K. m0 d U7 j# m9 y3 v$ {# D int t = ((ft < (float)MAXIMUM_CAPACITY) ? : h+ n/ P" T6 t. ]3 c" s9 i (int)ft : MAXIMUM_CAPACITY);) H+ S5 i7 K0 v) K
if (t > threshold)//如果t大于扩容的阀值就初始化阀值( \( g5 A- Y5 P" ^0 c
threshold = tableSizeFor(t);" N0 {" u* S2 |" Z: E
} , Q! s. y) p( D) a, C4 G' v7 O //如果这个map中的元素个数大于扩容的阀值就得扩容 3 j! W/ z- P+ _! k! | else if (s > threshold) 6 l" J3 O* }% g( i resize();" V d2 v5 ~: R6 w; h, p7 }# f! s
//将map中的key和value都添加到HashMap中; q2 l8 y Q g. E8 f7 K
for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {/ _( u* I8 K6 V2 G: t0 l6 e0 s
K key = e.getKey();* d f. I1 B7 f6 E" P
V value = e.getValue(); $ ~% d. R+ E: h, c0 Z putVal(hash(key), key, value, false, evict);- \% i0 H; L1 [6 M! r4 \9 u
} & o7 O" p" C5 ^0 [% V. B. o } & N' j& ?0 q2 {" E } 2 w, h8 [+ m- `' I' T6 o: s" t" |3 ]* t6 \! O3 A1 D
% T1 f* I. t# H' a3 o9 t# M
/**. } E8 O- P3 X, V2 Y
* 构造一个空的HashMap并指定初始容量和负载因子。 9 S! U: Y4 u4 ~- B2 h/ f! Q* # b* _1 B& _4 H8 K
**/8 Z" L2 y& W1 T! x
public HashMap(int initialCapacity, float loadFactor) { * P. v. K4 D# \) Z& y2 t6 H& e& y! m //如果初始容量小于0,抛出非法参数异常3 N T) c! a+ a7 G# E% N4 i# @
if (initialCapacity < 0)2 |: c4 C( a9 q; W1 ^
throw new IllegalArgumentException("Illegal initial capacity: " +; O, @& j& N r' | [9 D
initialCapacity); j" ^) T8 _/ n2 Z1 e //如果初始容量大于最大的容量也就是2^30,那么就按照最大的初始容量赋值。 ( q4 n$ f8 Y' v; c8 h if (initialCapacity > MAXIMUM_CAPACITY)9 `7 w9 j, P- X F% G8 [
initialCapacity = MAXIMUM_CAPACITY; ! Q+ Q* t* J0 y) i! I* \ //如果负载因子小于0或者是NaN(float NaN = 0.0f / 0.0f;)也会抛出非法参数异常 ' j* p5 |+ v5 t7 p$ K2 w if (loadFactor <= 0 || Float.isNaN(loadFactor)) 8 f/ l# a, B T throw new IllegalArgumentException("Illegal load factor: " + ; z& n7 O5 c1 A# v# g! S! Y loadFactor);7 K- q( N1 ^% l
this.loadFactor = loadFactor;3 j& h, g4 ~, ~+ D6 s+ s+ c) f
this.threshold = tableSizeFor(initialCapacity);: a1 ^0 C L0 t$ K2 ]1 U- H
}! ~* g% A, F: e# p
//如果只是指定了初始容量那么负载因子就是默认的0.75 1 z% G. J/ [3 W0 ipublic HashMap(int initialCapacity) {4 [0 E4 y% _. Z Y* \, T/ h
this(initialCapacity, DEFAULT_LOAD_FACTOR); # u0 o- M% f2 J3 t. w* X; b+ U } H; m( t2 i7 ~9 A, ]! W" i" I2 h- `+ }
/** ( y1 {( |7 a' X% q * 如果什么都没指定,也就是无参构造,则初始容量是16和负载因子是0.751 L/ a6 B' J: z: c( W2 l0 c
*/& s, i9 @: Y5 D+ o) ]( u
public HashMap() {- l. H( ~/ \* `" ]8 p+ |. }2 Z' u" c
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted ! ]4 m- a' I# B/ L- U8 n2 D }& Q t& Y( X; {, C+ d+ U
# p2 z* k' x* K
/** 6 ~9 y( \! D# p& A7 L# e2 M: X * 包含另一个Map的映射,如果被映射的Map是一个null会抛出空指针异常 j, i3 ^7 V5 a4 b: z0 K, E) N
* 负载因子时默认的1 e" w) b& M2 v
*/ % R, p- V7 W6 \9 X* Q public HashMap(Map<? extends K, ? extends V> m) {1 c( o! t: o" [! c. u( n
this.loadFactor = DEFAULT_LOAD_FACTOR;, U5 ~0 `( e4 q
putMapEntries(m, false); s5 [/ L# f: o4 `
} ) B9 B( K0 S+ h2 w# ]5 B 1 Y& f4 ^! w0 I9 r7 I( ofinal void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) { 2 f0 ]) W6 l! h) x4 F$ S" ]$ q int s = m.size();//s是map的大小 `/ [. F3 O" ~( y& G/ F7 p
if (s > 0) {1 }, i" E5 w" l
if (table == null) { // pre-size- U% l5 \& @, o. O
float ft = ((float)s / loadFactor) + 1.0F;. m# G4 Q" y/ E
int t = ((ft < (float)MAXIMUM_CAPACITY) ?! D5 O8 q" [0 ~$ s- }7 j6 G
(int)ft : MAXIMUM_CAPACITY);. l- n) W5 ?' u8 F/ ]6 E0 V
if (t > threshold)//如果t大于扩容的阀值就初始化阀值# z7 U, s8 |5 w- _2 `, P& T
threshold = tableSizeFor(t);2 a8 H: W6 ^8 [1 ~6 p( X
}3 o$ ~8 I- B# U. W Y a, e5 f% F
//如果这个map中的元素个数大于扩容的阀值就得扩容 1 C. W+ E3 ^$ h. K- m. t% h( h else if (s > threshold) , [1 `& O; ^! l resize();# k, O. V- |; N3 U
//将map中的key和value都添加到HashMap中 + \) a+ s& d% c' K: N$ ^8 ~# } for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {( H9 J& C$ l9 ^/ ~9 }5 i0 `
K key = e.getKey(); # r! f4 a* }, q1 S! @) |' K V value = e.getValue();+ S _& E7 D. V: a5 D" `
putVal(hash(key), key, value, false, evict); ! B% X+ J' I O0 [+ _3 z. k } 8 ^- A0 t4 ^6 q3 P3 s( P }' B' [8 X7 [/ ~& R2 ~% K
} ( D+ u7 Y. a# w7 ?1 Z3 n0 X& }7 Q% y- s; E7 I put方法
向HashMap中添加元素
2 x+ j+ B$ c; Y, m
# Y8 t' S* z" n
public V put(K key, V value) { \% D! {# l. Q7 V return putVal(hash(key), key, value, false, true); ; d1 a) t7 H' {$ W } & {; ^) ]+ C, _/ J t1 E. h5 B& q0 T: y1 p* V4 {" B
//扰动函数(哈希函数)1 N6 Y6 |( R! Y7 z8 ]1 \' h. v
static final int hash(Object key) {( \. g/ v% T! b
int h;2 F$ D* i9 l( V
//如果key是null,那么映射出来下标是0,否则将key的hashCode和h无符号右移16位做疑惑操作,使计算出的hash更加分散。: }5 k. g, H& q8 ]: X! z8 O
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);/ C: [$ K7 o. C, I. {# Y- v7 Z
}( I4 V1 l' n1 x- m' p
2 v; e [: C% ]! V: q4 T, K//onlyifAbsent默认是false,表示即使key存在也可以覆盖旧值。4 r) ^8 ^0 ~: h* f& m+ [+ U% Y
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, + J1 V% ] K9 J7 m3 F9 t3 N; } boolean evict) {4 `8 [- I/ ^+ K7 v" V! A
//n表示数组的长度,i表示数组的下标,p表示下标 i对应的Node 4 y9 n |1 Y' B$ r7 x ~ Node<K,V>[] tab; Node<K,V> p; int n, i;+ D$ ^* a! v- e, ?1 K7 t* n
if ((tab = table) == null || (n = tab.length) == 0)//如果数组为空需要进行扩容+ I6 S7 r9 y2 a# W9 Z
n = (tab = resize()).length;) |, i( P5 Q9 [2 q
if ((p = tab[i = (n - 1) & hash]) == null)//如果当前位置上没有节点那就新生成一个节点放上去 % X% ~3 P: U3 p: Q$ E tab = newNode(hash, key, value, null); ' Y5 ]' V: E0 U& [ else {//否则就代表这个位置已经有节点占用了, . s: D& ~; a. p Node<K,V> e; K k;//e表示临时节点 # j Y( L; E4 c( L1 z if (p.hash == hash && [$ G- M1 j6 O) \0 R
((k = p.key) == key || (key != null && key.equals(k)))) 2 o6 [4 B k" |) j // 如果key的hash和key本身的值都相等,直接把当前新增家电赋值给临时变量 e ( u* O% G0 S; C4 C# c% B e = p; ! r" e7 ]9 O: l //这里开始判断使用哪种类型的数据结构开始添加节点,如果是红黑树,就用红黑树的新增方式4 |- R$ y# z% ^4 z* G9 Z
else if (p instanceof TreeNode)" O. F0 t; ^" a
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);8 A9 |0 E6 _# ~3 E- u) _
else { , |6 a7 M+ U# S0 n' M) y //否则采用链表的方式新增节点,遍历到链表的末尾,追加到后面 * d& S& C; L6 ` for (int binCount = 0; ; ++binCount) {0 Z' X Y2 L" {1 U3 G
//p.next==null表示到链表尾部! d) _3 e, x1 z
if ((e = p.next) == null) { 6 m/ s) z. H7 q: B& \ //把新建的节点放在链表的最后 9 x1 W9 k, }$ t" Q+ ~) H p.next = newNode(hash, key, value, null);9 D: ^4 r0 ?2 c( [1 \4 R
//节点个数大于等于树化的阀值就需要转换成红黑树; s- t' v+ G" m: [ h, q) R
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st7 K1 k, H( a+ c8 z/ N' Q' }
treeifyBin(tab, hash);( g- w$ Q% y* G, K
break;//跳出循环 * N. h; Y& W. q+ L2 d6 N0 C } . ^: U6 l4 u3 M( _& J( \ //循环遍历中发现 有元素和新增节点相等就结束循环 8 D5 S* L2 k4 X1 S if (e.hash == hash &&* `7 U. }1 @+ D9 [3 ? a
((k = e.key) == key || (key != null && key.equals(k)))) 0 R1 A. }8 C& Q" W5 Z: k/ n+ z break;//相等就跳出循环 ' b/ O" U; |$ f: o( ~$ y p = e;) j6 G d% M6 @3 j
}. v2 j2 N; e; b' H
} & n( w v1 g$ S$ F4 K/ r //说明新节点的新增位置已经找到了 ) \/ p0 r4 A7 ~$ K if (e != null) { // existing mapping for key . ^2 L# W8 r( }, K: n. I% A; A' j4 W V oldValue = e.value; 6 Z2 ?" O; _$ `0 t2 `2 D" E //当onlyIfAbsent=false时才会覆盖# o7 V! ]- W) E. L: }& }! [* Z2 @- l
if (!onlyIfAbsent || oldValue == null)$ |* l/ U% R! V
e.value = value; [( C* K7 _; z afterNodeAccess(e);8 F+ D: P: W, T' e
//返回旧的值& t% L$ G1 o y( @
return oldValue;5 Y" }6 F2 B5 ~ ^. n& y; v4 i
}: e% q: I$ [" N0 E* w: h
}/ k8 y- Y# ^7 Z; Y( G9 K8 E
//结构修改计数器加一+ x# N3 \$ n$ P* {$ Z* c5 m
++modCount; 8 P3 N' f$ T3 k; ?) ]4 F" }( \ //如果实际大小 大于扩容的阀值就会再次扩容 " X; x( F) C$ T" m1 Q; ? J if (++size > threshold) ' b" w- L, g5 A' @4 ~ resize(); , m: r" d- x0 d( l- d5 ^ //插入后回调,具体实现交给了LinkedHashMap4 @% S \; m. L1 e! p; T
afterNodeInsertion(evict);9 U# R3 r9 O e8 C
return null; 4 n0 P. B& s' C( i8 Q5 P5 V2 k: } } ) z0 C1 h2 Y( c0 G' u红黑树新增节点的过程 . K* \2 ?* @% ^" u/ W ' A$ V8 O2 w( y" f: [0 ~+ Y( rfinal TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab," y6 m% F K- q5 `" d
int h, K k, V v) { ! b& o w" x8 O C6 H Class<?> kc = null;0 s) w8 Y6 ]# j6 y2 Z, B9 f
boolean searched = false;5 ?! y8 W$ n& V+ p; ~ n, u% b
//找到根节点& w0 X" ^1 F" b/ Y4 x2 h
TreeNode<K,V> root = (parent != null) ? root() : this; 9 j2 m d/ S, q* E9 q for (TreeNode<K,V> p = root;;) {" G" W* P- d6 l- L: P, \
int dir, ph; K pk;6 J& n- N1 t0 [- D) G
//如果p的hash大于 h,那p就在h的右边7 Z. s7 D- r* x! {, Y
if ((ph = p.hash) > h)+ N4 b; ]* E# ~. q
dir = -1;! A2 X& M& p; t( i
//如果p的hash小于 h,那p就在h的左边4 F& Z0 [0 H$ e" @# G. k/ ~3 t. p
else if (ph < h) 6 Q0 d4 U3 C9 \) ~1 c6 F) ] dir = 1;. ^) q2 {& Y; H2 h/ i
//如果将要新增的key已经在树中存在了那就直接返回p,这里代表插入的key没有实现Comparable接口,那就通过equals方法和值 比较是不是同一个key # v, t, P) |1 q1 a3 V8 n else if ((pk = p.key) == k || (k != null && k.equals(pk))) 2 Y) |3 g- D) u0 S return p;; e h+ n/ ^) g$ U. a/ I- p* @
//如果key实现了Comparable的话,不再用hashcode比较,需要用compareTo方法0 W' x1 l- k: }0 z$ ?
else if ((kc == null &&& Y I! A8 v6 f2 T
//如果key没有实现Comparable接口,那么 comparableClassFor(k)返回的就是null8 O1 K: k" M/ T
(kc = comparableClassFor(k)) == null) || % D" t; X( \; q) k8 ~/ }+ Z //当前节点的键和入参的键不相等 : q O8 J- O& s- Q$ {7 }+ s. _ (dir = compareComparables(kc, k, pk)) == 0) { ( ^0 J3 Z9 R. [) Z5 ~ if (!searched) { 4 j# k% Z- J" @: ]. r TreeNode<K,V> q, ch; ( m- L% b# m; K3 ]1 W) L searched = true;! P C' X/ L: w @* S
if (((ch = p.left) != null &&. X0 w9 c( M7 o) q- `- u
(q = ch.find(h, k, kc)) != null) ||# X7 W8 s o) N( E% y. G% P9 w
((ch = p.right) != null && 2 b/ y* k5 Y1 X0 j& e# l" M) Y (q = ch.find(h, k, kc)) != null))& p/ t2 d. w3 Q
return q; C$ k4 Q. f! {" x$ J
}; i9 l7 a% T/ Q: S7 V
dir = tieBreakOrder(k, pk); ' e, | S" ^0 w- D }. v* q3 E5 Y3 m& H
4 M* k& k' `9 x" J4 n# v
TreeNode<K,V> xp = p;( d2 J1 O; W* p- z1 t. J
if ((p = (dir <= 0) ? p.left : p.right) == null) {# B* d/ b8 \8 J" f1 j
Node<K,V> xpn = xp.next; $ A' i4 P/ n2 }3 k //生成新节点. q& r- ~. N! D
TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn);+ P! F8 K" ~* s8 `
if (dir <= 0), j. N7 y' d5 b' c
//如果dir=-1 新节点放在左子树 k% D1 x' _1 A$ L
xp.left = x; 7 b3 O" U: S9 A: [5 j" V else8 ~8 L- b5 A; t3 |$ q1 h
//如果dir=1 新节点放在右子树. E' y$ ]) S# I
xp.right = x;& ]2 O" U6 H7 u8 }+ N# n
//当前节点和新节点建立父子关系后前后关系 2 J- i2 X, b; |2 ]5 ~4 }& _ xp.next = x;3 e- s. J: q3 b
x.parent = x.prev = xp;( I9 Z! i L2 `1 y
if (xpn != null) ; I# S3 S9 [8 e$ G# P( Z ((TreeNode<K,V>)xpn).prev = x; ! I+ ^ X( G( z+ l moveRootToFront(tab, balanceInsertion(root, x)); / u- U- Y6 G/ K- j n //balanceInsertion 对红黑树进行着色或旋转,以达到更多的查找效率,着色或旋转的几种场景如下 : E' ^0 n" `# D) M. D5 [, k- s; O //着色:新节点总是为红色;如果新节点的父亲是黑色,则不需要重新着色;如果父亲是红色,那么必须 通过重新着色或者旋转的方法,再次达到红黑树的5个约束条件 ' ^& a% O3 @6 Z" D0 R //旋转: 父亲是红色,叔叔是黑色时,进行旋转! x6 v' d! W3 J0 M
//如果当前节点是父亲的右节点,则进行左旋9 Y1 b7 n K& q5 p) G+ Q
//如果当前节点是父亲的左节点,则进行右旋; k: V) u, L1 V8 X P
' S: W P; ^# ^3 j% M //moveRootToFront 方法是 把算出来的root放到根节点上, D* U. x R, b9 x) ]9 Y6 a
return null; / j1 X% F: P8 j/ U- j }7 q% X5 w) i- l, g( a# w
} 6 n4 P2 I& K) S" ]. V! i3 x3 p }; B4 i# m# n) }' C) P
1 k5 Z/ Q2 @# B$ u" A1 R8 P. O- r# q
首先判断新增的节点在红黑树上是不是已存在如果已存在就不在新增& I( {/ Z- h7 K
如果节点没有实现Comparable接口,使用equals方法判断 u8 ~6 T( Q O4 V0 n" l* z如果节点已经实现Comparable接口,使用CompareTo判断" s# Y9 x- d+ H" m5 k- ]9 {" [
新增节点如果已经在红黑树上,直接返回;不在的话判断新增节点是在当前节点的左边还是右边,左边比当前值小,右边比当前值大。 ! d( `5 T, s# t) K8 k递归前两步,知道当前节点的左子树或右子树为空时,停止递归,当前节点就是我们将要新增的父亲节点。 % |. U+ B% L# k! B; r- n将新增节点放在当前节点的左边或是右边,与当前节点建立父子几点关系。3 q* ^/ r# V# z: E- C" n
进行着色和旋转。3 }. e: G4 t; r1 L. ~$ D/ C
( s0 l) W+ R _! |) i, Tget方法 6 f0 g6 s- {. b c& l4 G. g8 P- B3 s. {$ H& x0 G; q* z t% W
public V get(Object key) { 6 k3 o" A1 ]6 b, T" I6 ? Node<K,V> e; 5 `; p8 J' {. o( { return (e = getNode(hash(key), key)) == null ? null : e.value; . ~ p% t) b$ @7 L0 Z" T% o }" \7 y( `4 b; b4 f4 J
. w4 m- @. _+ `* ?7 Z: z
/**0 P7 q* e8 S. j
* Implements Map.get and related methods 0 @$ W- t9 A6 [$ R8 C# P( ` *: m$ A2 ]& n4 j; `* U8 r
* @param hash hash for key( X% O! l+ q0 }/ P! K
* @param key the key# W4 U( M. y" K$ L- Q
* @return the node, or null if none ) p) J0 U& A- M6 S) L */ $ g5 j9 M& f: e% E5 z8 t1 M1 L# n final Node<K,V> getNode(int hash, Object key) { * z, c' i. U7 ~( L7 c Node<K,V>[] tab; Node<K,V> first, e; int n; K k; & ^$ b! E7 l7 ?& h) y //如果桶数组不为空,并且桶的长度>0,才进行下面的操作,否则就返回null H& a A7 |: A
if ((tab = table) != null && (n = tab.length) > 0 &&3 \) Q6 ]- l8 `8 w; T
(first = tab[(n - 1) & hash]) != null) {# I7 R- F8 M4 U9 I( n& i
//总是先检查第一个节点,如果第一个是我们要找的key就直接返回 0 E8 d# c/ p. f) T: N0 [ if (first.hash == hash && // always check first node , h3 L( l* }) o9 x% _+ U0 A6 r* C ((k = first.key) == key || (key != null && key.equals(k)))) + e* _/ T3 C% A" M6 y return first; . K: s' e: \; z! ?' @3 N if ((e = first.next) != null) {" e) T" u) `, h7 N4 ~6 o" E
//如果first是TreeNode类型那就调用红黑树获取节点的方法 : F' V7 s; J* Y6 ]4 B if (first instanceof TreeNode)1 @7 I5 Q- B( N [
return ((TreeNode<K,V>)first).getTreeNode(hash, key);( W$ V4 s' G5 b1 G
//否则就是一链表的方式查找值( k/ E8 p- q2 Y9 e! [& S
do { - j- D0 G! w' B4 S3 u: P //循环遍历,如果桶的hash和key与当前遍历的桶相等就返回。 : n) |/ f5 h- @/ }) [ if (e.hash == hash && / F' x5 Y: l9 t1 `6 ? ((k = e.key) == key || (key != null && key.equals(k)))) * `. M' A9 e; a return e; ; P3 k, |. I8 Z- V! _ } while ((e = e.next) != null);! l, U& h" n) [- J h4 n
} 9 f I! C; \/ o" s2 \ } 4 m! z5 k4 \9 q# ` z //以上都没找到就返回null 6 ~& z7 I) d8 H* f+ Y5 N return null;' y, t# i$ S5 T; ~
} 7 M( }' {! H; I) b& j ' Q9 h- Q' p. |' s查找主要分为三个步骤* A, m" `/ m% Z& g# M