因爲是淺析,因此我只分析經常使用的接口,注意是Android系統中的JAVA類,可能和JDK的源碼有區別。數組
首先從構造函數開始,app
1 /** 2 * Min capacity (other than zero) for a HashMap. Must be a power of two 3 * greater than 1 (and less than 1 << 30). 4 */ 5 private static final int MINIMUM_CAPACITY = 4; 6 7 /** 8 * Max capacity for a HashMap. Must be a power of two >= MINIMUM_CAPACITY. 9 */ 10 private static final int MAXIMUM_CAPACITY = 1 << 30; 11 12 /** 13 * An empty table shared by all zero-capacity maps (typically from default 14 * constructor). It is never written to, and replaced on first put. Its size 15 * is set to half the minimum, so that the first resize will create a 16 * minimum-sized table. 17 */ 18 private static final Entry[] EMPTY_TABLE 19 = new HashMapEntry[MINIMUM_CAPACITY >>> 1]; 20 21 /** 22 * The default load factor. Note that this implementation ignores the 23 * load factor, but cannot do away with it entirely because it's 24 * mentioned in the API. 25 * 26 * <p>Note that this constant has no impact on the behavior of the program, 27 * but it is emitted as part of the serialized form. The load factor of 28 * .75 is hardwired into the program, which uses cheap shifts in place of 29 * expensive division. 30 */ 31 static final float DEFAULT_LOAD_FACTOR = .75F; 32 33 /** 34 * The hash table. If this hash map contains a mapping for null, it is 35 * not represented this hash table. 36 */ 37 transient HashMapEntry<K, V>[] table; 38 39 /** 40 * The entry representing the null key, or null if there's no such mapping. 41 */ 42 transient HashMapEntry<K, V> entryForNullKey; 43 44 /** 45 * The number of mappings in this hash map. 46 */ 47 transient int size; 48 49 /** 50 * Incremented by "structural modifications" to allow (best effort) 51 * detection of concurrent modification. 52 */ 53 transient int modCount; 54 55 /** 56 * The table is rehashed when its size exceeds this threshold. 57 * The value of this field is generally .75 * capacity, except when 58 * the capacity is zero, as described in the EMPTY_TABLE declaration 59 * above. 60 */ 61 private transient int threshold; 62 63 public HashMap() { 64 table = (HashMapEntry<K, V>[]) EMPTY_TABLE; 65 threshold = -1; // Forces first put invocation to replace EMPTY_TABLE 66 } 67 68 public HashMap(int capacity) { 69 if (capacity < 0) { 70 throw new IllegalArgumentException("Capacity: " + capacity); 71 } 72 73 if (capacity == 0) { 74 @SuppressWarnings("unchecked") 75 HashMapEntry<K, V>[] tab = (HashMapEntry<K, V>[]) EMPTY_TABLE; 76 table = tab; 77 threshold = -1; // Forces first put() to replace EMPTY_TABLE 78 return; 79 } 80 81 if (capacity < MINIMUM_CAPACITY) { 82 capacity = MINIMUM_CAPACITY; 83 } else if (capacity > MAXIMUM_CAPACITY) { 84 capacity = MAXIMUM_CAPACITY; 85 } else { 86 capacity = Collections.roundUpToPowerOfTwo(capacity); 87 } 88 makeTable(capacity); 89 } 90 91 public HashMap(int capacity, float loadFactor) { 92 this(capacity); 93 94 if (loadFactor <= 0 || Float.isNaN(loadFactor)) { 95 throw new IllegalArgumentException("Load factor: " + loadFactor); 96 } 97 98 /* 99 * Note that this implementation ignores loadFactor; it always uses 100 * a load factor of 3/4. This simplifies the code and generally 101 * improves performance. 102 */ 103 }
經過三個構造函數的源碼,咱們能夠知道:less
既然是HashMapEntry數組實現的,咱們簡單看下這個Entry什麼樣,ide
static class HashMapEntry<K, V> implements Entry<K, V> { final K key; V value; final int hash; HashMapEntry<K, V> next; HashMapEntry(K key, V value, int hash, HashMapEntry<K, V> next) { this.key = key; this.value = value; this.hash = hash; this.next = next; } }
這裏注意關注next屬性,有必定經驗的朋友確定知道,這是單向鏈表的實現,因此實現HashMap的數組的每一項實際上是一個單向鏈表的Head,繼續往下看,函數
接下來咱們分析下put(K key, V value)方法,this
1 void addNewEntryForNullKey(V value) { 2 entryForNullKey = new HashMapEntry<K, V>(null, value, 0, null); 3 } 4 5 private V putValueForNullKey(V value) { 6 HashMapEntry<K, V> entry = entryForNullKey; 7 if (entry == null) { 8 addNewEntryForNullKey(value); 9 size++; 10 modCount++; 11 return null; 12 } else { 13 preModify(entry); 14 V oldValue = entry.value; 15 entry.value = value; 16 return oldValue; 17 } 18 } 19 20 private HashMapEntry<K, V>[] makeTable(int newCapacity) { 21 @SuppressWarnings("unchecked") HashMapEntry<K, V>[] newTable 22 = (HashMapEntry<K, V>[]) new HashMapEntry[newCapacity]; 23 table = newTable; 24 threshold = (newCapacity >> 1) + (newCapacity >> 2); // 3/4 capacity 25 return newTable; 26 } 27 28 private HashMapEntry<K, V>[] doubleCapacity() { 29 HashMapEntry<K, V>[] oldTable = table; 30 int oldCapacity = oldTable.length; 31 if (oldCapacity == MAXIMUM_CAPACITY) { 32 return oldTable; 33 } 34 int newCapacity = oldCapacity * 2; 35 HashMapEntry<K, V>[] newTable = makeTable(newCapacity); 36 if (size == 0) { 37 return newTable; 38 } 39 40 for (int j = 0; j < oldCapacity; j++) { 41 /* 42 * Rehash the bucket using the minimum number of field writes. 43 * This is the most subtle and delicate code in the class. 44 */ 45 HashMapEntry<K, V> e = oldTable[j]; 46 if (e == null) { 47 continue; 48 } 49 int highBit = e.hash & oldCapacity; 50 HashMapEntry<K, V> broken = null; 51 newTable[j | highBit] = e; 52 for (HashMapEntry<K, V> n = e.next; n != null; e = n, n = n.next) { 53 int nextHighBit = n.hash & oldCapacity; 54 if (nextHighBit != highBit) { 55 if (broken == null) 56 newTable[j | nextHighBit] = n; 57 else 58 broken.next = n; 59 broken = e; 60 highBit = nextHighBit; 61 } 62 } 63 if (broken != null) 64 broken.next = null; 65 } 66 return newTable; 67 } 68 69 @Override public V put(K key, V value) { 70 if (key == null) { 71 return putValueForNullKey(value); 72 } 73 74 int hash = Collections.secondaryHash(key); 75 HashMapEntry<K, V>[] tab = table; 76 int index = hash & (tab.length - 1); 77 for (HashMapEntry<K, V> e = tab[index]; e != null; e = e.next) { 78 if (e.hash == hash && key.equals(e.key)) { 79 preModify(e); 80 V oldValue = e.value; 81 e.value = value; 82 return oldValue; 83 } 84 } 85 86 // No entry for (non-null) key is present; create one 87 modCount++; 88 if (size++ > threshold) { 89 tab = doubleCapacity(); 90 index = hash & (tab.length - 1); 91 } 92 addNewEntry(key, value, hash, index); 93 return null; 94 } 95 96 void addNewEntry(K key, V value, int hash, int index) { 97 table[index] = new HashMapEntry<K, V>(key, value, hash, table[index]); 98 }
從put(K key, V value)的源碼咱們能夠獲得以下信息:spa
分析完put後,其餘如get、remove、containsKey等接口就大同小異了,在此直接略過。code
接下來咱們看下Set<K> keySet()接口:orm
@Override public Set<K> keySet() { Set<K> ks = keySet; return (ks != null) ? ks : (keySet = new KeySet()); } private final class KeySet extends AbstractSet<K> { public Iterator<K> iterator() { return newKeyIterator(); } public int size() { return size; } public boolean isEmpty() { return size == 0; } public boolean contains(Object o) { return containsKey(o); } public boolean remove(Object o) { int oldSize = size; HashMap.this.remove(o); return size != oldSize; } public void clear() { HashMap.this.clear(); } } Iterator<K> newKeyIterator() { return new KeyIterator(); } private final class KeyIterator extends HashIterator implements Iterator<K> { public K next() { return nextEntry().key; } }
從源碼中能夠得出以下結論:對象
知道HashMap的實現原理後,咱們就能夠知道他的優缺點了:
優勢:讀寫效率高,接近數組的索引方式。
缺陷:會佔用大量的無效內存,爲了減小碰撞,Entry數組的容量只能是2的N次冪,而且當entry數大於總容量的75%時就會擴容兩倍。
若有問題,歡迎指出!
轉載請註明出處。