public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable { //默認初始化大小,若是沒有給初始化值的話,大小就是16(2的n次冪),後面的擴容也是2的N次冪 static final int DEFAULT_INITIAL_CAPACITY = 16; //最大容量,2的30次方 static final int MAXIMUM_CAPACITY = 1 << 30; //系統默認的負載因子(沒懂其意義) static final float DEFAULT_LOAD_FACTOR = 0.75f; //存放數據的實體(重要) transient Entry[] table; //實際存放數據的量 transient int size; //閾值(沒懂),大小爲Entry的長度*loadFactor int threshold; //哈希表的負載因子(沒懂) final float loadFactor; //修改次數 transient volatile int modCount; //構建函數(初始化容量和hash表的負載) public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0)//小於零拋異常 throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); //保證Entry[]的大小不會超過最大值 if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); // 初始化各個值,除非initialCapacity恰好是2的N次冪,不然初始化Entry[]的容量確定比傳入的initialCapa //city值要大,而且應該是比initialCapacity大的第一個2的N次冪 int capacity = 1; while (capacity < initialCapacity) capacity <<= 1; this.loadFactor = loadFactor; threshold = (int)(capacity * loadFactor); table = new Entry[capacity]; init(); } //構造函數 public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } //構造函數 public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR); table = new Entry[DEFAULT_INITIAL_CAPACITY]; init(); } //構造函數 public HashMap(Map<? extends K, ? extends V> m) { //初始化新map的大小等參數 this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR); //copy數據,把m中的數據複製到新的集合裏面,putAllForCreate的細節在方法內解析 putAllForCreate(m); } void init() { } //一個自定義的hash算法 static int hash(int h) { h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); } //自定義的算法,返回的結果就是在Entry[]數組中的位置 static int indexFor(int h, int length) { return h & (length-1); } //返回具體數據的數量 public int size() { return size; } //判斷是否爲空 public boolean isEmpty() { return size == 0; } //獲取數據 public V get(Object key) { //key==null的話就找尋是否有key爲空值對應的value if (key == null) return getForNullKey(); int hash = hash(key.hashCode()); //這個for循環挺有意思,因爲hashmap是數組+鏈表,因此此循環其實是找到在Entry[]上的位置,而後循環這個 //位置上的鏈表,很像XY軸,定位到X軸後再循環在X軸上Y軸的數據 for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; //e內存儲的hash要和傳入的key的hash值相同(e內存儲的hash就是e中key的hash值),而且e中的key要和 //傳入的key相等,此時返回e的value if (e.hash == hash && ((k = e.key) == key || key.equals(k))) return e.value; } //不然爲空 return null; } //返回空鍵的值,按照以前的算法,傳入的key爲空的時候,因爲存放數值的函數處理的時候把key爲空時放到0處,因此 //搜尋地址在0處的鏈表就好了 private V getForNullKey() { for (Entry<K,V> e = table[0]; e != null; e = e.next) { if (e.key == null) return e.value; } return null; } //看是否有哪一個Entry內的key等於傳入的key,有的話爲true,不然爲false public boolean containsKey(Object key) { return getEntry(key) != null; } //獲取key對應的Entry final Entry<K,V> getEntry(Object key) { //key爲空的時候直接給0,不然計算會拋空指針異常 int hash = (key == null) ? 0 : hash(key.hashCode()); //遍歷整個鏈表 for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; //e的key與傳入的相等就返回此Entry if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } //不然爲空 return null; } //存入數據 public V put(K key, V value) { //key爲空的時候特殊處理 if (key == null) return putForNullKey(value); //計算key的hash值 int hash = hash(key.hashCode()); //經過key定位到數組的位置 int i = indexFor(hash, table.length); //遍歷i上的鏈表 for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; //若是找到有同樣的(同一個key),就替換此處的值,只替換此處Entry內的value屬性值 if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; //不知道幹啥的 e.recordAccess(this); //返回之前的value PS:有些API手冊上沒註明會返回原來的值 return oldValue; } } //修改次數+1 modCount++; //發現Entry[]的i位置上的鏈表沒有一樣的key,就在i上添加值,具體的添加細節看下面的方法註釋 addEntry(hash, key, value, i); //而後返回NULL return null; } //key爲空的時候存放value private V putForNullKey(V value) { //遍歷Entry[]0處的鏈表 for (Entry<K,V> e = table[0]; e != null; e = e.next) { //功能同上個方法 if (e.key == null) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } //同上,0處沒發現有等於null的key就添加一個值 modCount++; addEntry(0, null, value, 0); return null; } //最後一個構造函數new Map(map)的業務方法 private void putForCreate(K key, V value) { //若key爲空仍然用0,不然就計算出hash值 int hash = (key == null) ? 0 : hash(key.hashCode()); //在Entry[]上的位置 int i = indexFor(hash, table.length); //遍歷此位置上的鏈表 for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; //替換value if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { e.value = value; return; } } //沒找到同樣的key就建立一個Entry createEntry(hash, key, value, i); } //第四個構造函數直接調用的方法 private void putAllForCreate(Map<? extends K, ? extends V> m) { //用迭代器(感受能夠用while(i.hasNext())),遍歷m內的全部元素 for (Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) { Map.Entry<? extends K, ? extends V> e = i.next(); //把每一個迭代的值用如下方法處理,具體看下面方法的註釋 putForCreate(e.getKey(), e.getValue()); } } //從新修改map的大小 void resize(int newCapacity) { //將當前的Entry[]拿出來 Entry[] oldTable = table; //舊數組的長度(數組的長度,不是數值的數量) int oldCapacity = oldTable.length; //若就數組的長度是最大值 if (oldCapacity == MAXIMUM_CAPACITY) { //修改臨界值 threshold = Integer.MAX_VALUE; return; } //用新的傳入長度構建一個Entry[] Entry[] newTable = new Entry[newCapacity]; //複製,把原來Entry[]內的數據都複製到新的Entry[]內 transfer(newTable); //替換掉舊的數組 table = newTable; //修改閾值 threshold = (int)(newCapacity * loadFactor); } //複製,把原來Entry[]內的數據都複製到新的Entry[]內 void transfer(Entry[] newTable) { //把當前數組(舊數組)「存盤」 Entry[] src = table; //新數組的長度 int newCapacity = newTable.length; //遍歷舊的數組 for (int j = 0; j < src.length; j++) { //舊數組的數據「存盤」 Entry<K,V> e = src[j]; if (e != null) { //清空位置爲j上的全部數據 src[j] = null; //開始循環以前j位置上的鏈表 do { //這個循環有點意思,文字表述有點困難,後續補圖 //首先Entry是個鏈表,它內部的next指向另外一個Entry //理解以上的東西后,如下循環以這個鏈表上的第一個數據爲例: //先把鏈表從第二個值開始的鏈表(當前循環值的後一個)「存盤」(1),而後在新的數組上計算出此鏈表 //上第一個值應該存放的位置i(2),而後把這個數組i上的數據拼接到此鏈表第一個數據以後,造成一個新 //的鏈表(3),再把拼接後的鏈表放到數組的i處(4),而後把從第二個值開始的鏈表賦給e(5),接下 //來就是繼續循環,知道e==null爲止(6)。而後再循環此數組上的下一個鏈表 Entry<K,V> next = e.next; //1 int i = indexFor(e.hash, newCapacity);//2 e.next = newTable[i];//3 newTable[i] = e;//4 e = next;//5 } while (e != null);//6 } //copy完以後數據的位置是有變化的,先不說在新數組上的位置變了,連在鏈表上的位置也變了 //總體的調整容量思路就是:建立一個具備新容量的數組,而後把舊數組的值所有複製到新的數組裏面,同時清 //空舊數組的值(消除指針),等待回收 } } //把m的值copy到原來的數組內,若是key相同就替換掉原來的value,若是key不一樣則加入 public void putAll(Map<? extends K, ? extends V> m) { int numKeysToBeAdded = m.size(); //傳入的m爲空,就返回空 if (numKeysToBeAdded == 0) return; //若是m的size大於當前的閾值,則經過計算擴大後的m.size()會不會比table.length大,若是大則執行後續操做 //ps:小小的計算了一下,發現只要m.size>閾值,newCapacity < targetCapacity就幾乎是恆成立的 //大概就是4/3*m.size<table.length<4/3*m.size+1,在這種狀況下newCapacity 纔可能大於targetCapacity if (numKeysToBeAdded > threshold) { int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1); if (targetCapacity > MAXIMUM_CAPACITY) targetCapacity = MAXIMUM_CAPACITY; int newCapacity = table.length; while (newCapacity < targetCapacity) //給原來的table.length擴容 newCapacity <<= 1; if (newCapacity > table.length) //調整容量的大小 resize(newCapacity); } //開始迭代 for (Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) { Map.Entry<? extends K, ? extends V> e = i.next(); //把迭代的值扔進原來的put中,執行結果很像jquery的深拷貝(有就替換,沒有就添加) put(e.getKey(), e.getValue()); } } //移除Entry[]上e.key=key的Entry,並返回此Entry處的value public V remove(Object key) { Entry<K,V> e = removeEntryForKey(key); return (e == null ? null : e.value); } //按照Key移除的業務方法 final Entry<K,V> removeEntryForKey(Object key) { //key爲空hash直接給0 int hash = (key == null) ? 0 : hash(key.hashCode()); //數組的地址 int i = indexFor(hash, table.length); //存幾個盤 Entry<K,V> prev = table[i]; Entry<K,V> e = prev; //開始循環i處的鏈表,先不看if語句,先理解這個循環,其實很簡單,設兩個指針,以e爲標準,prev一直是e的 //前一個指針,循環一輪這倆指針就日後移動一格 while (e != null) { Entry<K,V> next = e.next; Object k; //找到對應的值 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { //修改次數+1 modCount++; //數量-1 size--; //若是e==prev(這種狀況只會是刪除i處的鏈表的第一個值),就把鏈表上的第二個值放到第一個上 if (prev == e) table[i] = next; else //不然的話,就把prev指向下一個數據的指針移到第三個值身上(跳過第二個值e) prev.next = next; e.recordRemoval(this); //返回這個Entry return e; } 向後移動指針 prev = e; e = next; } return e; } //同上,只不過此時傳入的是Object對象,還要判斷一下,若是此對象不是Entry格式的,就返回空 final Entry<K,V> removeMapping(Object o) { if (!(o instanceof Map.Entry)) return null; Map.Entry<K,V> entry = (Map.Entry<K,V>) o; Object key = entry.getKey(); int hash = (key == null) ? 0 : hash(key.hashCode()); int i = indexFor(hash, table.length); Entry<K,V> prev = table[i]; Entry<K,V> e = prev; while (e != null) { Entry<K,V> next = e.next; if (e.hash == hash && e.equals(entry)) { modCount++; size--; if (prev == e) table[i] = next; else prev.next = next; e.recordRemoval(this); return e; } prev = e; e = next; } return e; } //清倉 public void clear() { modCount++; Entry[] tab = table; for (int i = 0; i < tab.length; i++) tab[i] = null; size = 0; } //找找是不有這個value這個值 public boolean containsValue(Object value) { //等於空時特殊處理 if (value == null) return containsNullValue(); //遍歷數組的每一個值 Entry[] tab = table; for (int i = 0; i < tab.length ; i++) //遍歷每一個鏈表上的每一個值 for (Entry e = tab[i] ; e != null ; e = e.next) if (value.equals(e.value)) return true; return false; } //跟上面同樣,只不過專門找尋value爲空的value,而後返回boolen值 private boolean containsNullValue() { Entry[] tab = table; for (int i = 0; i < tab.length ; i++) for (Entry e = tab[i] ; e != null ; e = e.next) if (e.value == null) return true; return false; } //返回一個hashmap對象,這個對象自己是新的,內部的Entry[]數組也是新的,可是,key和value是引用過來的 //實際上,putAllForCreate(this)這個方法也是引用各變量的地址,內部屬性並無在堆內存中開闢新的空間 public Object clone() { HashMap<K,V> result = null; try { //在內存中開闢空間,用於存放一個新的hashmap result = (HashMap<K,V>)super.clone(); } catch (CloneNotSupportedException e) { } //初始化長度等各類屬性 result.table = new Entry[table.length]; result.entrySet = null; result.modCount = 0; result.size = 0; result.init(); //this指當前的Hashmap,就是舊的hashmap,此方法上面有註釋,只是把key,value,hash和i存入新的hashmap內 result.putAllForCreate(this); return result; } //這就是Entry的真面目,內部類,也叫桶,我以爲叫鏈條更有意思 - -|| static class Entry<K,V> implements Map.Entry<K,V> { final K key; V value; //就是由於每一個桶裏都有另外一個桶,至關於指向(其實就是指向),另外一個桶,這樣就把整個鏈條穿起來 Entry<K,V> next; final int hash; //構造方法 Entry(int h, K k, V v, Entry<K,V> n) { value = v; next = n; key = k; hash = h; } //沒啥說的 public final K getKey() { return key; } //沒啥說的 public final V getValue() { return value; } //沒啥說的 public final V setValue(V newValue) { V oldValue = value; value = newValue; return oldValue; } //自定義equels方法 public final boolean equals(Object o) { //判斷類型 if (!(o instanceof Map.Entry)) return false; //強轉一個 Map.Entry e = (Map.Entry)o; //當前key Object k1 = getKey(); //傳入的key Object k2 = e.getKey(); //判斷key和value是否相同,此處要求內存地址或者內容相同 if (k1 == k2 || (k1 != null && k1.equals(k2))) { Object v1 = getValue(); Object v2 = e.getValue(); if (v1 == v2 || (v1 != null && v1.equals(v2))) return true; } return false; } //重寫hashcode方法,爲了equals準備 public final int hashCode() { return (key==null ? 0 : key.hashCode()) ^ (value==null ? 0 : value.hashCode()); } //沒啥說的 public final String toString() { return getKey() + "=" + getValue(); } //沒啥說的,沒懂有什麼用 void recordAccess(HashMap<K,V> m) { } void recordRemoval(HashMap<K,V> m) { } } //建立新的桶 void addEntry(int hash, K key, V value, int bucketIndex) { //原來bucketIndex處的鏈表存盤 Entry<K,V> e = table[bucketIndex]; //建立新的桶,而後把以前的鏈表鏈接到新的桶上,造成新的鏈表 table[bucketIndex] = new Entry<K,V>(hash, key, value, e); //數據量超過閾值以後就擴大table.length,而後要從新計算閾值,老規矩仍是大於等於2 * table.length的第 //一個2的n次冪*3/4 if (size++ >= threshold) resize(2 * table.length); } //建立新的桶,和上個方法差很少,只是不用擴大閾值,由於調用這個方法的方法都知道,建立的桶是不會超過閾值的 void createEntry(int hash, K key, V value, int bucketIndex) { Entry<K,V> e = table[bucketIndex]; table[bucketIndex] = new Entry<K,V>(hash, key, value, e); size++; } //實現迭代器 private abstract class HashIterator<E> implements Iterator<E> { Entry<K,V> next; //下一個值 int expectedModCount; // 用來存儲改變的次數 int index; //當前指針 Entry<K,V> current; //當前值 //構造方法 HashIterator() { //存儲修改次數 expectedModCount = modCount; if (size > 0) { // advance to first entry Entry[] t = table; //看起來是用來循環數組上的每個鏈表,可是不知道爲何在構造器裏這樣寫 while (index < t.length && (next = t[index++]) == null) ; } } //經常使用迭代器判斷方法 public final boolean hasNext() { return next != null; } //返回迭代的下一個桶的值 final Entry<K,V> nextEntry() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); //next賦給一個新的桶(next在構造器裏被賦值過) Entry<K,V> e = next; if (e == null) throw new NoSuchElementException(); //next的指針日後移一個單位,並判斷這個鏈表是否到最後一個值 if ((next = e.next) == null) { //若是是最後一個值,則index指向下一個數組的位置,next則指向下一個數組的第一個桶 Entry[] t = table; while (index < t.length && (next = t[index++]) == null) ; } //e賦給當前值 current = e; //返回當前值 return e; } //移除當前值 public void remove() { if (current == null) throw new IllegalStateException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); Object k = current.key; current = null; HashMap.this.removeEntryForKey(k); expectedModCount = modCount; } } //把以上的類當父類繼承,next()只返回value private final class ValueIterator extends HashIterator<V> { public V next() { return nextEntry().value; } } //一樣繼承,可是隻返回桶的key private final class KeyIterator extends HashIterator<K> { public K next() { return nextEntry().getKey(); } } //返回桶 private final class EntryIterator extends HashIterator<Map.Entry<K,V>> { public Map.Entry<K,V> next() { return nextEntry(); } } //沒加訪問權限符,默認是friendly,只有同類和同包中的類才能訪問 Iterator<K> newKeyIterator() { return new KeyIterator(); } Iterator<V> newValueIterator() { return new ValueIterator(); } Iterator<Map.Entry<K,V>> newEntryIterator() { return new EntryIterator(); } //生命一個Set private transient Set<Map.Entry<K,V>> entrySet = null; //賦值ks一個new KeySet() public Set<K> keySet() { Set<K> ks = keySet; return (ks != null ? ks : (keySet = new KeySet())); } //繼承抽象Set,一個Set的視圖 private final class KeySet extends AbstractSet<K> { //實現接口的方法,返回以前寫好的方法,返回桶的key public Iterator<K> iterator() { return newKeyIterator(); } //下面全是重寫 public int size() { return size; } public boolean contains(Object o) { return containsKey(o); } public boolean remove(Object o) { return HashMap.this.removeEntryForKey(o) != null; } public void clear() { HashMap.this.clear(); } } //把桶的value轉化成爲Collection類型,返回Collection類型的視圖 public Collection<V> values() { Collection<V> vs = values; return (vs != null ? vs : (values = new Values())); } //定義一個類,實現AbstractCollection接口,至關於建立一個AbstractCollection類型的類 private final class Values extends AbstractCollection<V> { public Iterator<V> iterator() { return newValueIterator(); } public int size() { return size; } public boolean contains(Object o) { return containsValue(o); } public void clear() { HashMap.this.clear(); } } //返回一個entrySet public Set<Map.Entry<K,V>> entrySet() { return entrySet0(); } //entrySet的視圖 private Set<Map.Entry<K,V>> entrySet0() { Set<Map.Entry<K,V>> es = entrySet; return es != null ? es : (entrySet = new EntrySet()); } //其實和keySet是同樣的,只不過這裏的迭代器中的next方法返回的是entry,keyset中的迭代器方法返回的是key private final class EntrySet extends AbstractSet<Map.Entry<K,V>> { public Iterator<Map.Entry<K,V>> iterator() { return newEntryIterator(); } public boolean contains(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry<K,V> e = (Map.Entry<K,V>) o; Entry<K,V> candidate = getEntry(e.getKey()); return candidate != null && candidate.equals(e); } public boolean remove(Object o) { return removeMapping(o) != null; } public int size() { return size; } public void clear() { HashMap.this.clear(); } } //不懂 private void writeObject(java.io.ObjectOutputStream s) throws IOException { Iterator<Map.Entry<K,V>> i = (size > 0) ? entrySet0().iterator() : null; s.defaultWriteObject(); s.writeInt(table.length); s.writeInt(size); if (i != null) { while (i.hasNext()) { Map.Entry<K,V> e = i.next(); s.writeObject(e.getKey()); s.writeObject(e.getValue()); } } } private static final long serialVersionUID = 362498820763181265L; private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); int numBuckets = s.readInt(); table = new Entry[numBuckets]; init(); int size = s.readInt(); for (int i=0; i<size; i++) { K key = (K) s.readObject(); V value = (V) s.readObject(); putForCreate(key, value); } } // These methods are used when serializing HashSets int capacity() { return table.length; } float loadFactor() { return loadFactor; } }
最後一段迭代器解釋的不是很清楚,能夠逆向來想,collection和set接口都有定義這些類的方法、入參和返回參數,那若是要返回這些類型的對象,就須要一個類去實現這些接口,而後被返回。而這些接口中都有定義迭代器的方法,那就須要返回一個迭代器的對象,此時就寫一個HashIterator抽象類,並實現Iterator接口,但又因爲需求的不一樣(有些要返回value,有些要返回key,有些又要返回entry),因此就定義爲抽象類,這樣就不用實現Iterator下的全部方法,下面再寫三個類,繼承HashIterator,並實現next()方法,用來返回不一樣的需求值。而後咱們在定義一個方法,用來返回剛纔編寫的類的對象,將此方法放入collection和set的實現類的Iterator方法中,這樣就返回了相應的視圖java
PS:不是太瞭解爲何要多加一個方法去返回三個迭代器對象,目測應該是出於訪問權限的關係,可能其餘的類中也須要調用到這個方法,因此設置成friendly(沒加訪問權限符,默認爲friendly)。jquery