對於一個儲存類的分析,無非從兩點入手:儲存用的資料結構,儲存的運行機制。
資料結構:
數組
/** * The table, resized as necessary. Length MUST Always be a power of two. */ transient Entry[] table;
Entry
鏈表格式。
也即,HashMap中採用的是“連結法”來處理碰撞問題的。
運行機制:
put、get
put方法
public V put(K key, V value) { if (key == null) return putForNullKey(value); int hash = hash(key.hashCode()); 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))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; }
get方法
public V get(Object key) { if (key == null) return getForNullKey(); int hash = hash(key.hashCode()); for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) return e.value; } return null; }
可以看出,重要的一句代碼為:
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
這句代碼的作用是判斷兩個Key對象是否“相同”,這裡的相同的意思是指兩個Key完全可以不分彼此,可以作為同一個Key使用。
在put方法中,如果兩個Key的hashCode相同,且equals符合,這裡是把原有的的value替換成新的value。
測試代碼:
package test.junit;import java.util.HashMap;import junit.framework.TestCase;/** * HashMap中判斷兩個Key是否相同:先判斷hashCode,在判斷是否equals * e.hash == hash && ((k = e.key) == key || key.equals(k)) * @author xuefeng * */public class HashMapTest extends TestCase {/** * v1和v4的hashCode相同,但是equals不符合,所以v1和v4會組成一個鏈表。 */public void testKey1() {HashMap<Key, Value> map = new HashMap<Key, Value>();Key k1 = new Key(1);Key k4 = new Key(4);Value v1 = new Value(1);Value v4 = new Value(4);map.put(k1, v1);map.put(k4, v4);assertEquals(v1, map.get(k1));assertEquals(v4, map.get(k4));}/** * v1和v4的hashCode相同,而且equals也符合,所以v1會被v4替換掉,k1和k4指向同一個v4。 */public void testKey2() {HashMap<Key2, Value> map = new HashMap<Key2, Value>();Key2 k1 = new Key2(1);Key2 k4 = new Key2(4);Value v1 = new Value(1);Value v4 = new Value(4);map.put(k1, v1);map.put(k4, v4); // 這裡會替換掉v1//assertEquals(v1, map.get(k1));assertEquals(v4, map.get(k1));assertEquals(v4, map.get(k4));}}class Key {public int m;public Key(int m) {this.m = m;}@Overridepublic int hashCode() {return m%3;}}class Value {public int m;public Value(int m) {this.m = m;}}class Key2 {public int m;public Key2(int m) {this.m = m;}@Overridepublic int hashCode() {return m%3;}@Overridepublic boolean equals(Object obj) {if(obj == null || !(obj instanceof Key2)) return false;Key2 other = (Key2)obj;return this.hashCode() == other.hashCode();}}