1. 程式人生 > >IdentityHashMap類原始碼解析

IdentityHashMap類原始碼解析

package java.util; import java.io.*; public class IdentityHashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, java.io.Serializable, Cloneable { /** * 預設容量 */ private static final int DEFAULT_CAPACITY = 32; /** * 最小容量 */ private static final
int MINIMUM_CAPACITY = 4; /** * 最大容量 */ private static final int MAXIMUM_CAPACITY = 1 << 29; /** * 輸出儲存結構 */ private transient Object[] table; /** * 鍵值對個數 * @serial */ private int size; /** * 修改次數 */ private transient int modCount; /** * 更新容器時候的閾值= (capacity * load factor). */
private transient int threshold; /** * Value representing null keys inside tables. */ private static final Object NULL_KEY = new Object(); /** * Use NULL_KEY for key if it is null. */ private static Object maskNull(Object key) { return (key == null ? NULL_KEY : key); } /** * Returns internal representation of null key back to caller as null. */
private static Object unmaskNull(Object key) { return (key == NULL_KEY ? null : key); } /** * 建構函式 */ public IdentityHashMap() { init(DEFAULT_CAPACITY); } /** * 建構函式 * * @param expectedMaxSize the expected maximum size of the map * @throws IllegalArgumentException if <tt>expectedMaxSize</tt> is negative */ public IdentityHashMap(int expectedMaxSize) { if (expectedMaxSize < 0) throw new IllegalArgumentException("expectedMaxSize is negative: " + expectedMaxSize); init(capacity(expectedMaxSize)); } /** * Returns the appropriate capacity for the specified expected maximum * size. Returns the smallest power of two between MINIMUM_CAPACITY * and MAXIMUM_CAPACITY, inclusive, that is greater than * (3 * expectedMaxSize)/2, if such a number exists. Otherwise * returns MAXIMUM_CAPACITY. If (3 * expectedMaxSize)/2 is negative, it * is assumed that overflow has occurred, and MAXIMUM_CAPACITY is returned. */ private int capacity(int expectedMaxSize) { // Compute min capacity for expectedMaxSize given a load factor of 2/3 int minCapacity = (3 * expectedMaxSize)/2; // Compute the appropriate capacity int result; if (minCapacity > MAXIMUM_CAPACITY || minCapacity < 0) { result = MAXIMUM_CAPACITY; } else { result = MINIMUM_CAPACITY; while (result < minCapacity) result <<= 1; } return result; } /** * init */ private void init(int initCapacity) { // assert (initCapacity & -initCapacity) == initCapacity; // power of 2 // assert initCapacity >= MINIMUM_CAPACITY; // assert initCapacity <= MAXIMUM_CAPACITY; threshold = (initCapacity * 2)/3; // 進行擴容時候的閾值 table = new Object[2 * initCapacity]; // 2倍,表示key value相鄰儲存 } /** * 建構函式,m集合元素加入到當前集合中 */ public IdentityHashMap(Map<? extends K, ? extends V> m) { // Allow for a bit of growth this((int) ((1 + m.size()) * 1.1)); putAll(m); } /** * size */ public int size() { return size; } /** * isEmpty */ public boolean isEmpty() { return size == 0; } /** * Returns index for Object x. */ private static int hash(Object x, int length) { int h = System.identityHashCode(x); // Multiply by -127, and left-shift to use least bit as part of hash return ((h << 1) - (h << 8)) & (length - 1); } /** *迴圈的方式,找到下一個key的id */ private static int nextKeyIndex(int i, int len) { return (i + 2 < len ? i + 2 : 0); } /** * V get(Object key) */ public V get(Object key) { Object k = maskNull(key); Object[] tab = table; int len = tab.length; int i = hash(k, len); // 根據hash計算應該在陣列中的id while (true) { Object item = tab[i]; // 當前key if (item == k) // 比較是否相等,相等下一個位置就是value return (V) tab[i + 1]; if (item == null) return null; i = nextKeyIndex(i, len); // 上面不滿足,根據上一個i 更新 i } } /** * containsKey 和 get 很類似 */ public boolean containsKey(Object key) { Object k = maskNull(key); Object[] tab = table; int len = tab.length; int i = hash(k, len); while (true) { Object item = tab[i]; if (item == k) return true; if (item == null) return false; i = nextKeyIndex(i, len); } } /** * containsValue */ public boolean containsValue(Object value) { Object[] tab = table; for (int i = 1; i < tab.length; i += 2) // 順序遍歷,1 3 5 7 9 位置是value if (tab[i] == value && tab[i - 1] != null) return true; return false; } /** * containsMapping 和containsKey 很類似 */ private boolean containsMapping(Object key, Object value) { Object k = maskNull(key); Object[] tab = table; int len = tab.length; int i = hash(k, len); while (true) { Object item = tab[i]; if (item == k) return tab[i + 1] == value; // i 位置相等時 key相等,i+1 相等時value相等,比較的是引用相等 if (item == null) return false; i = nextKeyIndex(i, len); } } /** *put(K key, V value) */ public V put(K key, V value) { Object k = maskNull(key); Object[] tab = table; int len = tab.length; int i = hash(k, len); Object item; while ( (item = tab[i]) != null) { if (item == k) { V oldValue = (V) tab[i + 1]; tab[i + 1] = value; // key 存在更新value return oldValue; } i = nextKeyIndex(i, len); } modCount++; tab[i] = k; // key 不存在加入 tab[i + 1] = value; if (++size >= threshold) resize(len); // len == 2 * current capacity. return null; } /** * resize 需要移動大量元素,效率很低 * * @param newCapacity the new capacity, must be a power of two. */ private void resize(int newCapacity) { // assert (newCapacity & -newCapacity) == newCapacity; // power of 2 int newLength = newCapacity * 2; Object[] oldTable = table; int oldLength = oldTable.length; if (oldLength == 2*MAXIMUM_CAPACITY) { // can't expand any further if (threshold == MAXIMUM_CAPACITY-1) throw new IllegalStateException("Capacity exhausted."); threshold = MAXIMUM_CAPACITY-1; // Gigantic map! return; } if (oldLength >= newLength) return; Object[] newTable = new Object[newLength]; threshold = newLength / 3; for (int j = 0; j < oldLength; j += 2) { Object key = oldTable[j]; if (key != null) { Object value = oldTable[j+1]; oldTable[j] = null; oldTable[j+1] = null; int i = hash(key, newLength); while (newTable[i] != null) // 找到可以插入的位置 i = nextKeyIndex(i, newLength); newTable[i] = key; newTable[i + 1] = value; } } table = newTable; } /** * putAll * @param m mappings to be stored in this map * @throws NullPointerException if the specified map is null */ public void putAll(Map<? extends K, ? extends V> m) { int n = m.size(); if (n == 0) return; if (n > threshold) // conservatively pre-expand resize(capacity(n)); for (Entry<? extends K, ? extends V> e : m.entrySet()) put(e.getKey(), e.getValue()); } /** * remove */ public V remove(Object key) { Object k = maskNull(key); Object[] tab = table; int len = tab.length; int i = hash(k, len); while (true) { Object item = tab[i]; if (item == k) { modCount++; size--; V oldValue = (V) tab[i + 1]; tab[i + 1] = null; tab[i] = null; closeDeletion(i); return oldValue; } if (item == null) return null; i = nextKeyIndex(i, len); } } /** * removeMapping */ private boolean removeMapping(Object key, Object value) { Object k = maskNull(key); Object[] tab = table; int len = tab.length; int i = hash(k, len); while (true) { Object item = tab[i]; if (item == k) { if (tab[i + 1] != value) return false; modCount++; size--; tab[i] = null; tab[i + 1] = null; closeDeletion(i); return true; } if (item == null) return false; i = nextKeyIndex(i, len); } } /** * closeDeletion 作用:i位置元素刪除了,更新後面元素的 位置,這裡一個原因就是減少地址衝突 */ private void closeDeletion(int d) { // Adapted from Knuth Section 6.4 Algorithm R Object[] tab = table; int len = tab.length; // Look for items to swap into newly vacated slot // starting at index immediately following deletion, // and continuing until a null slot is seen, indicating // the end of a run of possibly-colliding keys. Object item; for (int i = nextKeyIndex(d, len); (item = tab[i]) != null; i = nextKeyIndex(i, len) ) { // The following test triggers if the item at slot i (which // hashes to be at slot r) should take the spot vacated by d. // If so, we swap it in, and then continue with d now at the // newly vacated i. This process will terminate when we hit // the null slot at the end of this run. // The test is messy because we are using a circular table. int r = hash(item, len); if ((i < r && (r <= d || d <= i)) || (r <= d && d <= i)) { tab[d] = item; tab[d + 1] = tab[i + 1]; tab[i] = null; tab[i + 1] = null; d = i; } } } /** * clear */ public void clear() { modCount++; Object[] tab = table; for (int i = 0; i < tab.length; i++) tab[i] = null; size = 0; } /** * equals */ public boolean equals(Object o) { if (o == this) { return true; } else if (o instanceof IdentityHashMap) { IdentityHashMap m = (IdentityHashMap) o; if (m.size() != size) return false; Object[] tab = m.table; for (int i = 0; i < tab.length; i+=2) { Object k = tab[i]; if (k != null && !containsMapping(k, tab[i + 1])) return false; } return true; } else if (o instanceof Map) { Map m = (Map)o; return entrySet().equals(m.entrySet()); } else { return false; // o is not a Map } } /** * hashCode */ public int hashCode() { int result = 0; Object[] tab = table; for (int i = 0; i < tab.length; i +=2) { Object key = tab[i]; if (key != null) { Object k = unmaskNull(key); result += System.identityHashCode(k) ^ System.identityHashCode(tab[i + 1]); } } return result; } /** * clone */ public Object clone() { try { IdentityHashMap<K,V> m = (IdentityHashMap<K,V>) super.clone(); m.entrySet = null; m.table = table.clone(); return m; } catch (CloneNotSupportedException e) { throw new InternalError(); } } // 迭代器 private abstract class IdentityHashMapIterator<T> implements Iterator<T> { int index = (size != 0 ? 0 : table.length); // current slot. int expectedModCount = modCount; // to support fast-fail int lastReturnedIndex = -1; // to allow remove() boolean indexValid; // To avoid unnecessary next computation Object[] traversalTable = table; // reference to main table or copy public boolean hasNext() { Object[] tab = traversalTable; for (int i = index; i < tab.length; i+=2) { Object key = tab[i]; if (key != null) { index = i; return indexValid = true; } } index = tab.length; return false; } protected int nextIndex() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); if (!indexValid && !hasNext()) throw new NoSuchElementException(); indexValid = false; lastReturnedIndex = index; index += 2; return lastReturnedIndex; } public void remove() { if (lastReturnedIndex == -1) throw new IllegalStateException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); expectedModCount = ++modCount; int deletedSlot = lastReturnedIndex; lastReturnedIndex = -1; // back up index to revisit new contents after deletion index = deletedSlot; indexValid = false; // Removal code proceeds as in closeDeletion except that // it must catch the rare case where an element already // seen is swapped into a vacant slot that will be later // traversed by this iterator. We cannot allow future // next() calls to return it again. The likelihood of // this occurring under 2/3 load factor is very slim, but // when it does happen, we must make a copy of the rest of // the table to use for the rest of the traversal. Since // this can only happen when we are near the end of the table, // even in these rare cases, this is not very expensive in // time or space. Object[] tab = traversalTable; int len = tab.length; int d = deletedSlot; K key = (K) tab[d]; tab[d] = null; // vacate the slot tab[d + 1] = null; // If traversing a copy, remove in real table. // We can skip gap-closure on copy. if (tab != IdentityHashMap.this.table) { IdentityHashMap.this.remove(key); expectedModCount = modCount; return; } size--; Object item; for (int i = nextKeyIndex(d, len); (item = tab[i]) != null; i = nextKeyIndex(i, len)) { int r = hash(item, len); // See closeDeletion for explanation of this conditional if ((i < r && (r <= d || d <= i)) || (r <= d && d <= i)) { // If we are about to swap an already-seen element // into a slot that may later be returned by next(), // then clone the rest of table for use in future // next() calls. It is OK that our copy will have // a gap in the "wrong" place, since it will never // be used for searching anyway. if (i < deletedSlot && d >= deletedSlot && traversalTable == IdentityHashMap.this.table) { int remaining = len - deletedSlot; Object[] newTable = new Object[remaining]; System.arraycopy(tab, deletedSlot, newTable, 0, remaining); traversalTable = newTable; index = 0; } tab[d] = item; tab[d + 1] = tab[i + 1]; tab[i] = null; tab[i + 1] = null; d = i; } } } } private class KeyIterator extends IdentityHashMapIterator<K> { public K next() { return (K) unmaskNull(traversalTable[nextIndex()]); } } private class ValueIterator extends IdentityHashMapIterator<V> { public V next() { return (V) traversalTable[nextIndex() + 1]; } } private class EntryIterator extends IdentityHashMapIterator<Map.Entry<K,V>> { private Entry lastReturnedEntry = null; public Map.Entry<K,V> next() { lastReturnedEntry = new Entry(nextIndex()); return lastReturnedEntry; } public void remove() { lastReturnedIndex = ((null == lastReturnedEntry) ? -1 : lastReturnedEntry.index); super.remove(); lastReturnedEntry.index = lastReturnedIndex; lastReturnedEntry = null; } private class Entry implements Map.Entry<K,V> { private int index; private Entry(int index) { this.index = index; } public K getKey() { checkIndexForEntryUse(); return (K) unmaskNull(traversalTable[index]); } public V getValue() { checkIndexForEntryUse(); return (V) traversalTable[index+1]; } public V setValue(V value) { checkIndexForEntryUse(); V oldValue = (V) traversalTable[index+1]; traversalTable[index+1] = value; // if shadowing, force into main table if (traversalTable != IdentityHashMap.this.table) put((K) traversalTable[index], value); return oldValue; } public boolean equals(Object o) { if (index < 0) return super.equals(o); if (!(o instanceof Map.Entry)) return false; Map.Entry e = (Map.Entry)o; return (e.getKey() == unmaskNull(traversalTable[index]) && e.getValue() == traversalTable[index+1]); } public int hashCode() { if (lastReturnedIndex < 0) return super.hashCode(); return (System.identityHashCode(unmaskNull(traversalTable[index])) ^ System.identityHashCode(traversalTable[index+1])); } public String toString() { if (index < 0) return super.toString(); return (unmaskNull(traversalTable[index]) + "=" + traversalTable[index+1]); } private void checkIndexForEntryUse() { if (index < 0) throw new IllegalStateException("Entry was removed"); } } } // Views /** * This field is initialized to contain an instance of the entry set * view the first time this view is requested. The view is stateless, * so there's no reason to create more than one. */ private transient Set<Map.Entry<K,V>> entrySet = null; /** * keySet */ public Set<K> keySet() { Set<K> ks = keySet; if (ks != null) return ks; else return keySet = new KeySet(); } private class KeySet extends AbstractSet<K> { public Iterator<K> iterator() { return new KeyIterator(); } public int size() { return size; } public boolean contains(Object o) { return containsKey(o); } public boolean remove(Object o) { int oldSize = size; IdentityHashMap.this.remove(o); return size != oldSize; } /* * Must revert from AbstractSet's impl to AbstractCollection's, as * the former contains an optimization that results in incorrect * behavior when c is a smaller "normal" (non-identity-based) Set. */ public boolean removeAll(Collection<?> c) { boolean modified = false; for (Iterator<K> i = iterator(); i.hasNext(); ) { if (c.contains(i.next())) { i.remove(); modified = true; } } return modified; } public void clear() { IdentityHashMap.this.clear(); } public int hashCode() { int result = 0; for (K key : this) result += System.identityHashCode(key); return result; } } /** * values */ public Collection<V> values() { Collection<V> vs = values; if (vs != null) return vs; else return values = new Values(); } private class Values extends AbstractCollection<V> { public Iterator<V> iterator() { return new ValueIterator(); } public int size() { return size; } public boolean contains(Object o) { return containsValue(o); } public boolean remove(Object o) { for (Iterator<V> i = iterator(); i.hasNext(); ) { if (i.next() == o) { i.remove(); return true; } } return false; } public void clear() { IdentityHashMap.this.clear(); } } /** *entrySet */ public Set<Map.Entry<K,V>> entrySet() { Set<Map.Entry<K,V>> es = entrySet; if (es != null) return es; else return entrySet = new EntrySet(); } private class EntrySet extends AbstractSet<Map.Entry<K,V>> { public Iterator<Map.Entry<K,V>> iterator() { return new EntryIterator(); } public boolean contains(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry entry = (Map.Entry)o; return containsMapping(entry.getKey(), entry.getValue()); } public boolean remove(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry entry = (Map.Entry)o; return removeMapping(entry.getKey(), entry.getValue()); } public int size() { return size; } public void clear() { IdentityHashMap.this.clear(); } /* * Must revert from AbstractSet's impl to AbstractCollection's, as * the former contains an optimization that results in incorrect * behavior when c is a smaller "normal" (non-identity-based) Set. */ public boolean removeAll(Collection<?> c) { boolean modified = false; for (Iterator<Map.Entry<K,V>> i = iterator(); i.hasNext(); ) { if (c.contains(i.next())) { i.remove(); modified = true; } } return modified; } public Object[] toArray() { int size = size(); Object[] result = new Object[size]; Iterator<Map.Entry<K,V>> it = iterator(); for (int i = 0; i < size; i++) result[i] = new AbstractMap.SimpleEntry<>(it.next()); return result; } @SuppressWarnings("unchecked") public <T> T[] toArray(T[] a) { int size = size(); if (a.length < size) a = (T[])java.lang.reflect.Array .newInstance(a.getClass().getComponentType(), size); Iterator<Map.Entry<K,V>> it = iterator(); for (int i = 0; i < size; i++) a[i] = (T) new AbstractMap.SimpleEntry<>(it.next()); if (a.length > size) a[size] = null; return a; } } private static final long serialVersionUID = 8188218128353913216L; /** * Save the state of the <tt>IdentityHashMap</tt> instance to a stream * (i.e., serialize it). * * @serialData The <i>size</i> of the HashMap (the number of key-value * mappings) (<tt>int</tt>), followed by the key (Object) and * value (Object) for each key-value mapping represented by the * IdentityHashMap. The key-value mappings are emitted in no * particular order. */ private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { // Write out and any hidden stuff s.defaultWriteObject(); // Write out size (number of Mappings) s.writeInt(size); // Write out keys and values (alternating) Object[] tab = table; for (int i = 0; i < tab.length; i += 2) { Object key = tab[i]; if (key != null) { s.writeObject(unmaskNull(key)); s.writeObject(tab[i + 1]); } } } /** * Reconstitute the <tt>IdentityHashMap</tt> instance from a stream (i.e., * deserialize it). */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { // Read in any hidden stuff s.defaultReadObject(); // Read in size (number of Mappings) int size = s.readInt(); // Allow for 33% growth (i.e., capacity is >= 2* size()). init(capacity((size*4)/3)); // Read the keys and values, and put the mappings in the table for (int i=0; i<size; i++) { K key = (K) s.readObject(); V value = (V) s.readObject(); putForCreate(key, value); } } /** * The put method for readObject. It does not resize the table, * update modCount, etc. */ private void putForCreate(K key, V value) throws IOException { K k = (K)maskNull(key); Object[] tab = table; int len = tab.length; int i = hash(k, len); Object item; while ( (item = tab[i]) != null) { if (item == k) throw new java.io.StreamCorruptedException(); i = nextKeyIndex(i, len); } tab[i] = k; tab[i + 1] = value; } }