1 仍是老習慣,一邊看,一邊添加註釋,但願堅持下去,HashMap的基本源碼進行了分析,內部一些接口和設計還沒來得及看 2
3 1、成員 4
5 一、transient Entry[] table; 6
7 HashMap內部維護了一個內部類-Entry,用來存放鍵值對,這個Entry實現了Map.Entry這一Map的內部接口Entry,HashMap本質上來說是由數組和Entry鏈表組成的數據結構 8
9 二、 static final float DEFAULT_LOAD_FACTOR = 0.75f; 10
11 加載因子,加載因子越大,hash表(即Entry數組)所佔空間越少,但會影響查詢性能(由於須要經過鏈表一個挨一個向下查詢),加載因子越小,hash表(即Entry數組)所佔空間越多,這時查詢效率較高,可是hash表所佔空間較多 12
13 三、static final int DEFAULT_INITIAL_CAPACITY = 16; 14
15 四、/**
16
17 * The next size value at which to resize (capacity * load factor). 18 * @serial
19 */
20 int threshold; 21
22 五、static final int MAXIMUM_CAPACITY = 1 << 30; 23
24 六、static final int DEFAULT_INITIAL_CAPACITY = 16; 25
26 七、final float loadFactor 27
28 決定何時進行擴容 29
30 2、方法 31
32 1、核心構造方法 33
34 public HashMap(int initialCapacity, float loadFactor) { 35 if (initialCapacity < 0) 36 throw new IllegalArgumentException("Illegal initial capacity: " +
37 initialCapacity); 38 if (initialCapacity > MAXIMUM_CAPACITY) 39 initialCapacity = MAXIMUM_CAPACITY; 40 if (loadFactor <= 0 || Float.isNaN(loadFactor)) 41 throw new IllegalArgumentException("Illegal load factor: " +
42 loadFactor); 43
44 // Find a power of 2 >= initialCapacity
45 int capacity = 1; 46 while (capacity < initialCapacity) 47 capacity <<= 1; 48
49 this.loadFactor = loadFactor; 50 threshold = (int)(capacity * loadFactor); 51 table = new Entry[capacity]; //capacity表明數組的長度
52 init(); 53 } 54
55 2、在key對象的hashCodr()方法的基礎上再作hash,避免一些很差的hashCode()方法 56
57 //Null keys always map to hash 0, 若是key爲null,那麼hash()方法的到的hash值爲0,再調用indexFor方法獲得的數組的索引值也爲0,因此key爲null的Entry存在數組下標爲0的位置
58
59 static int hash(int h) { 60 // This function ensures that hashCodes that differ only by 61 // constant multiples at each bit position have a bounded 62 // number of collisions (approximately 8 at default load factor).
63 h ^= (h >>> 20) ^ (h >>> 12); 64 return h ^ (h >>> 7) ^ (h >>> 4); 65 } 66
67 3、根據2中得到的hash值和數組的長度獲得Entry對應的數組的索引 68
69 static int indexFor(int h, int length) { 70 return h & (length-1); //屏蔽高位,保證與操做後最大值爲length-1
71 } 72
73 4、根據key獲取value 74
75 public V get(Object key) { 76 if (key == null) 77 return getForNullKey(); //若是key爲null則直接取index爲0的Entry對應的value值
78 int hash = hash(key.hashCode()); //生成Entryhash值
79 for (Entry<K,V> e = table[indexFor(hash, table.length)]; //獲取header
80 e != null; 81 e = e.next) { //鏈表向下走
82 Object k; 83 if (e.hash == hash && ((k = e.key) == key || key.equals(k))) //判斷key是否相同
84 return e.value; 85 } 86 return null; 87 } 88
89 5、獲取key爲null的key對應的值(注意:這裏使用在鏈表中查找的方式,由於index爲0的鏈表上不是隻有key爲null的Entry) 90
91 private V getForNullKey() { 92 for (Entry<K,V> e = table[0]; e != null; e = e.next) { 93 if (e.key == null) 94 return e.value; 95 } 96 return null; 97 } 98
99 6、添加鍵值對 100
101 public V put(K key, V value) { 102
103 /*若是key存在則對value進行修改並將原value返回*/
104 if (key == null) 105 return putForNullKey(value); 106 int hash = hash(key.hashCode()); 107 int i = indexFor(hash, table.length); 108 for (Entry<K,V> e = table[i]; e != null; e = e.next) { 109 Object k; 110 if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { 111 V oldValue = e.value; 112 e.value = value; 113 e.recordAccess(this); 114 return oldValue; 115 } 116 } 117
118 /*若是key不存在則新增鍵值對*/
119
120 modCount++; 121 addEntry(hash, key, value, i); 122 return null; 123 } 124
125 7、新增長鍵值對 126
127 void addEntry(int hash, K key, V value, int bucketIndex) { 128 Entry<K,V> e = table[bucketIndex]; //獲取header
129 table[bucketIndex] = new Entry<K,V>(hash, key, value, e); //new一個新Entry,並將後指針指向原header,新加的Entry成爲新header
130 if (size++ >= threshold) //若是元素個數超過閾值,則進行擴容
131 resize(2 * table.length); //擴容爲原來的2倍
132 } 133
134 8、擴容 135
136 void resize(int newCapacity) { 137 Entry[] oldTable = table; 138 int oldCapacity = oldTable.length; 139 if (oldCapacity == MAXIMUM_CAPACITY) { 140 threshold = Integer.MAX_VALUE; 141 return; 142 } 143
144 Entry[] newTable = new Entry[newCapacity]; //擴容爲新capacity
145 transfer(newTable); //將全部的Entry遷移到新的數組中去
146 table = newTable; 147 threshold = (int)(newCapacity * loadFactor); //從新計算閾值
148 } 149
150 9、將全部Entry遷移到新數組中 151
152 void transfer(Entry[] newTable) { 153 Entry[] src = table; 154 int newCapacity = newTable.length; 155
156 /*遍歷Entry數組的0-(size-1)的索引對應的Entry鏈表,並將鏈表上的Entry從新計算在新數組中的索引並遷移到新數組的Entry鏈中*/
157 for (int j = 0; j < src.length; j++) { 158 Entry<K,V> e = src[j]; 159 if (e != null) { 160 src[j] = null; //for GC
161 do { //遍歷處理某個索引上的Entry鏈
162 Entry<K,V> next = e.next; 163 int i = indexFor(e.hash, newCapacity); //從新計算索引
164
165 /*將全部Entry分別放到應該放到的indexEntry鏈上*/
166 e.next = newTable[i]; 167 newTable[i] = e; 168 e = next; 169 } while (e != null); 170 } 171 } 172 }