public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable { /** * HashMap默认大小,必须为2的倍数。 * The default initial capacity - MUST be a power of two. */ static final int DEFAULT_INITIAL_CAPACITY = 4; /** * HashMap的最大的容量。如果在构造器中传入比它更大的值,则使用这个最大值。 * The maximum capacity, used if a higher value is implicitly specified * by either of the constructors with arguments. * MUST be a power of two <= 1<<30. */ static final int MAXIMUM_CAPACITY = 1 << 30; /** * 默认负载因子。 * The load factor used when none specified in constructor. */ static final float DEFAULT_LOAD_FACTOR = 0.75f; /** * 空数组,在数组没有增大前的初始化值。 * An empty table instance to share when the table is not inflated. */ static final HashMapEntry<?,?>[] EMPTY_TABLE = {}; /** * 键值对数组,在需要的时候调整大小。这个数组的大小必须是2的倍数。 * The table, resized as necessary. Length MUST Always be a power of two. */ transient HashMapEntry<K,V>[] table = (HashMapEntry<K,V>[]) EMPTY_TABLE; /** * map中键值对的个数,也就是HashMap的大小。 * The number of key-value mappings contained in this map. */ transient int size; /** * 临界值,如果达到这个临界值,需要调整数组大小。计算方法是:capacity * factor * The next size value at which to resize (capacity * load factor). * @serial */ // If table == EMPTY_TABLE then this is the initial capacity at which the // table will be created when inflated. int threshold; /** * hash表的使用的负载因子。在android平台,一直使用0.75作为负载因子,并忽略构造器传入的负载因子参数。 * The load factor for the hash table. * @serial */ // Android-Note: We always use a load factor of 0.75 and ignore any explicitly // selected values. final float loadFactor = DEFAULT_LOAD_FACTOR; /** * * The number of times this HashMap has been structurally modified * Structural modifications are those that change the number of mappings in * the HashMap or otherwise modify its internal structure (e.g., * rehash). This field is used to make iterators on Collection-views of * the HashMap fail-fast. (See ConcurrentModificationException). */ transient int modCount; /** * Constructs an empty <tt>HashMap</tt> with the specified initial * capacity and load factor. * * @param initialCapacity the initial capacity * @param loadFactor the load factor * @throws IllegalArgumentException if the initial capacity is negative * or the load factor is nonpositive */ public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); if (initialCapacity > MAXIMUM_CAPACITY) { initialCapacity = MAXIMUM_CAPACITY; } else if (initialCapacity < DEFAULT_INITIAL_CAPACITY) { initialCapacity = DEFAULT_INITIAL_CAPACITY; } if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); // Android-Note: We always use the default load factor of 0.75f. // This might appear wrong but it's just awkward design. We always call // inflateTable() when table == EMPTY_TABLE. That method will take "threshold" // to mean "capacity" and then replace it with the real threshold (i.e, multiplied with // the load factor). threshold = initialCapacity; init(); } /** * Constructs an empty <tt>HashMap</tt> with the specified initial * capacity and the default load factor (0.75). * * @param initialCapacity the initial capacity. * @throws IllegalArgumentException if the initial capacity is negative. */ public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } /** * Constructs an empty <tt>HashMap</tt> with the default initial capacity * (16) and the default load factor (0.75). */ public HashMap() { this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR); } /** * Constructs a new <tt>HashMap</tt> with the same mappings as the * specified <tt>Map</tt>. The <tt>HashMap</tt> is created with * default load factor (0.75) and an initial capacity sufficient to * hold the mappings in the specified <tt>Map</tt>. * * @param m the map whose mappings are to be placed in this map * @throws NullPointerException if the specified map is null */ public HashMap(Map<? extends K, ? extends V> m) { this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR); inflateTable(threshold); putAllForCreate(m); } private static int roundUpToPowerOf2(int number) { // assert number >= 0 : "number must be non-negative"; int rounded = number >= MAXIMUM_CAPACITY ? MAXIMUM_CAPACITY : (rounded = Integer.highestOneBit(number)) != 0 ? (Integer.bitCount(number) > 1) ? rounded << 1 : rounded : 1; return rounded; } /** * Inflates the table. */ private void inflateTable(int toSize) { // Find a power of 2 >= toSize int capacity = roundUpToPowerOf2(toSize); // Android-changed: Replace usage of Math.min() here because this method is // called from the <clinit> of runtime, at which point the native libraries // needed by Float.* might not be loaded. float thresholdFloat = capacity * loadFactor; if (thresholdFloat > MAXIMUM_CAPACITY + 1) { thresholdFloat = MAXIMUM_CAPACITY + 1; } threshold = (int) thresholdFloat; table = new HashMapEntry[capacity]; } // internal utilities /** * Initialization hook for subclasses. This method is called * in all constructors and pseudo-constructors (clone, readObject) * after HashMap has been initialized but before any entries have * been inserted. (In the absence of this method, readObject would * require explicit knowledge of subclasses.) */ void init() { } /** * 算出hash对应的数组的index。 * 数组长度length是2的倍数,因此length - 1转换为二进制的形式一直是类似于这样的:“1111111”,设此值的二进制长度为bitSize, * 则{@code bitSize = Integer.toBinaryString(length - 1).length()}。 * 传入的hash与length - 1求逻辑与,即为从低位开始截取hash的bitSize长度的二进制值,同时保证了index是小于length的。 * Returns index for hash code h. */ static int indexFor(int h, int length) { // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2"; return h & (length-1); } /** * Returns the number of key-value mappings in this map. * * @return the number of key-value mappings in this map */ public int size() { return size; } /** * Returns <tt>true</tt> if this map contains no key-value mappings. * * @return <tt>true</tt> if this map contains no key-value mappings */ public boolean isEmpty() { return size == 0; } /** * Returns the value to which the specified key is mapped, * or {@code null} if this map contains no mapping for the key. * * <p>More formally, if this map contains a mapping from a key * {@code k} to a value {@code v} such that {@code (key==null ? k==null : * key.equals(k))}, then this method returns {@code v}; otherwise * it returns {@code null}. (There can be at most one such mapping.) * * <p>A return value of {@code null} does not <i>necessarily</i> * indicate that the map contains no mapping for the key; it's also * possible that the map explicitly maps the key to {@code null}. * The {@link #containsKey containsKey} operation may be used to * distinguish these two cases. * * @see #put(Object, Object) */ public V get(Object key) { if (key == null) return getForNullKey(); Entry<K,V> entry = getEntry(key); return null == entry ? null : entry.getValue(); } /** * Offloaded version of get() to look up null keys. Null keys map * to index 0. This null case is split out into separate methods * for the sake of performance in the two most commonly used * operations (get and put), but incorporated with conditionals in * others. */ private V getForNullKey() { if (size == 0) { return null; } for (HashMapEntry<K,V> e = table[0]; e != null; e = e.next) { if (e.key == null) return e.value; } return null; } /** * Returns <tt>true</tt> if this map contains a mapping for the * specified key. * * @param key The key whose presence in this map is to be tested * @return <tt>true</tt> if this map contains a mapping for the specified * key. */ public boolean containsKey(Object key) { return getEntry(key) != null; } /** * Returns the entry associated with the specified key in the * HashMap. Returns null if the HashMap contains no mapping * for the key. */ final Entry<K,V> getEntry(Object key) { if (size == 0) { return null; } int hash = (key == null) ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key); for (HashMapEntry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } return null; } /** * 首先数组为{}时,先扩容。 * key为null时,调用putForNullKey;否则: * 如果数组中有该键,替换之;否则modCount + 1,调用addEntry方法添加一条记录。 * * Associates the specified value with the specified key in this map. * If the map previously contained a mapping for the key, the old * value is replaced. * * @param key key with which the specified value is to be associated * @param value value to be associated with the specified key * @return the previous value associated with <tt>key</tt>, or * <tt>null</tt> if there was no mapping for <tt>key</tt>. * (A <tt>null</tt> return can also indicate that the map * previously associated <tt>null</tt> with <tt>key</tt>.) */ public V put(K key, V value) { if (table == EMPTY_TABLE) { inflateTable(threshold); } if (key == null) return putForNullKey(value); int hash = sun.misc.Hashing.singleWordWangJenkinsHash(key); //找到hash对应的index int i = indexFor(hash, table.length); //只要next不为null,在链表中遍历 for (HashMapEntry<K,V> e = table[i]; e != null; e = e.next) { Object k; //先比较hash,如果相等,再判断引用相等或equals方法为true,可认为查询到了key对应的键值对。 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方法添加一条记录。 addEntry(hash, key, value, i); return null; } /** * put一个key为null的值。如果数组中已存在该key,替换之;否则modCount + 1, * 调用addEntry方法添加一条记录。 * Offloaded version of put for null keys */ private V putForNullKey(V value) { for (HashMapEntry<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; } } modCount++; addEntry(0, null, value, 0); return null; } /** * This method is used instead of put by constructors and * pseudoconstructors (clone, readObject). It does not resize the table, * check for comodification, etc. It calls createEntry rather than * addEntry. */ private void putForCreate(K key, V value) { int hash = null == key ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key); int i = indexFor(hash, table.length); /** * Look for preexisting entry for key. This will never happen for * clone or deserialize. It will only happen for construction if the * input Map is a sorted map whose ordering is inconsistent w/ equals. */ for (HashMapEntry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { e.value = value; return; } } createEntry(hash, key, value, i); } private void putAllForCreate(Map<? extends K, ? extends V> m) { for (Entry<? extends K, ? extends V> e : m.entrySet()) putForCreate(e.getKey(), e.getValue()); } /** * Rehashes the contents of this map into a new array with a * larger capacity. This method is called automatically when the * number of keys in this map reaches its threshold. * * If current capacity is MAXIMUM_CAPACITY, this method does not * resize the map, but sets threshold to Integer.MAX_VALUE. * This has the effect of preventing future calls. * * @param newCapacity the new capacity, MUST be a power of two; * must be greater than current capacity unless current * capacity is MAXIMUM_CAPACITY (in which case value * is irrelevant). */ void resize(int newCapacity) { HashMapEntry[] oldTable = table; int oldCapacity = oldTable.length; if (oldCapacity == MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return; } HashMapEntry[] newTable = new HashMapEntry[newCapacity]; transfer(newTable); table = newTable; threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1); } /** * Transfers all entries from current table to newTable. */ void transfer(HashMapEntry[] newTable) { int newCapacity = newTable.length; for (HashMapEntry<K,V> e : table) { while(null != e) { HashMapEntry<K,V> next = e.next; int i = indexFor(e.hash, newCapacity); e.next = newTable[i]; newTable[i] = e; e = next; } } } /** * Copies all of the mappings from the specified map to this map. * These mappings will replace any mappings that this map had for * any of the keys currently in the specified map. * * @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 numKeysToBeAdded = m.size(); if (numKeysToBeAdded == 0) return; if (table == EMPTY_TABLE) { inflateTable((int) Math.max(numKeysToBeAdded * loadFactor, threshold)); } /* * Expand the map if the map if the number of mappings to be added * is greater than or equal to threshold. This is conservative; the * obvious condition is (m.size() + size) >= threshold, but this * condition could result in a map with twice the appropriate capacity, * if the keys to be added overlap with the keys already in this map. * By using the conservative calculation, we subject ourself * to at most one extra resize. */ if (numKeysToBeAdded > threshold) { int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1); if (targetCapacity > MAXIMUM_CAPACITY) targetCapacity = MAXIMUM_CAPACITY; int newCapacity = table.length; while (newCapacity < targetCapacity) newCapacity <<= 1; if (newCapacity > table.length) resize(newCapacity); } for (Entry<? extends K, ? extends V> e : m.entrySet()) put(e.getKey(), e.getValue()); } /** * Removes the mapping for the specified key from this map if present. * * @param key key whose mapping is to be removed from the map * @return the previous value associated with <tt>key</tt>, or * <tt>null</tt> if there was no mapping for <tt>key</tt>. * (A <tt>null</tt> return can also indicate that the map * previously associated <tt>null</tt> with <tt>key</tt>.) */ public V remove(Object key) { Entry<K,V> e = removeEntryForKey(key); return (e == null ? null : e.getValue()); } /** * Removes and returns the entry associated with the specified key * in the HashMap. Returns null if the HashMap contains no mapping * for this key. */ final Entry<K,V> removeEntryForKey(Object key) { if (size == 0) { return null; } int hash = (key == null) ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key); int i = indexFor(hash, table.length); HashMapEntry<K,V> prev = table[i]; HashMapEntry<K,V> e = prev; while (e != null) { HashMapEntry<K,V> next = e.next; Object k; if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { modCount++; size--; if (prev == e) table[i] = next; else prev.next = next; e.recordRemoval(this); return e; } prev = e; e = next; } return e; } /** * Special version of remove for EntrySet using {@code Map.Entry.equals()} * for matching. */ final Entry<K,V> removeMapping(Object o) { if (size == 0 || !(o instanceof Map.Entry)) return null; Entry<K,V> entry = (Entry<K,V>) o; Object key = entry.getKey(); int hash = (key == null) ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key); int i = indexFor(hash, table.length); HashMapEntry<K,V> prev = table[i]; HashMapEntry<K,V> e = prev; while (e != null) { HashMapEntry<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; } /** * Removes all of the mappings from this map. * The map will be empty after this call returns. */ public void clear() { modCount++; Arrays.fill(table, null); size = 0; } /** * Returns <tt>true</tt> if this map maps one or more keys to the * specified value. * * @param value value whose presence in this map is to be tested * @return <tt>true</tt> if this map maps one or more keys to the * specified value */ public boolean containsValue(Object value) { if (value == null) return containsNullValue(); HashMapEntry[] tab = table; for (int i = 0; i < tab.length ; i++) for (HashMapEntry e = tab[i] ; e != null ; e = e.next) if (value.equals(e.value)) return true; return false; } /** * Special-case code for containsValue with null argument */ private boolean containsNullValue() { HashMapEntry[] tab = table; for (int i = 0; i < tab.length ; i++) for (HashMapEntry e = tab[i] ; e != null ; e = e.next) if (e.value == null) return true; return false; } /** * Returns a shallow copy of this <tt>HashMap</tt> instance: the keys and * values themselves are not cloned. * * @return a shallow copy of this map */ public Object clone() { java.util.HashMap<K,V> result = null; try { result = (java.util.HashMap<K,V>)super.clone(); } catch (CloneNotSupportedException e) { // assert false; } if (result.table != EMPTY_TABLE) { result.inflateTable(Math.min( (int) Math.min( size * Math.min(1 / loadFactor, 4.0f), // we have limits... java.util.HashMap.MAXIMUM_CAPACITY), table.length)); } result.entrySet = null; result.modCount = 0; result.size = 0; result.init(); result.putAllForCreate(this); return result; } /** @hide */ // Android added. static class HashMapEntry<K,V> implements Entry<K,V> { final K key; V value; HashMapEntry<K,V> next; int hash; /** * Creates new entry. */ HashMapEntry(int h, K k, V v, HashMapEntry<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; } public final boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Entry e = (Entry)o; Object k1 = getKey(); Object k2 = e.getKey(); 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; } public final int hashCode() { return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue()); } public final String toString() { return getKey() + "=" + getValue(); } /** * This method is invoked whenever the value in an entry is * overwritten by an invocation of put(k,v) for a key k that's already * in the HashMap. */ void recordAccess(java.util.HashMap<K,V> m) { } /** * This method is invoked whenever the entry is * removed from the table. */ void recordRemoval(java.util.HashMap<K,V> m) { } } /** * * 添加一条记录到数组中,这个方法负责是否对数组扩容。 * Adds a new entry with the specified key, value and hash code to * the specified bucket. It is the responsibility of this * method to resize the table if appropriate. * * Subclass overrides this to alter the behavior of put method. */ void addEntry(int hash, K key, V value, int bucketIndex) { /* 如果Map大小大于等于临界值,这时表明可能数组中的链表可能太多了,长度也可能太大了,查找的性能会下降。 如果bucketIndex对应的数组中的值不为null,这时如果添加一条新纪录,需要在链表前加一条。 综合以上2个考虑,如果同时满足这2个条件,需要扩容。 */ if ((size >= threshold) && (null != table[bucketIndex])) { //大小是2的倍数 resize(2 * table.length); hash = (null != key) ? sun.misc.Hashing.singleWordWangJenkinsHash(key) : 0; bucketIndex = indexFor(hash, table.length); } createEntry(hash, key, value, bucketIndex); } /** * @param hash 键的hash值 * @param bucketIndex 根据hash值算出的数组table的index * * 这个方法专门用来新建HashMapEntry对象。原来的table[bucketIndex]作为e的next,形成一个链表。同时size + 1。 * * Like addEntry except that this version is used when creating entries * as part of Map construction or "pseudo-construction" (cloning, * deserialization). This version needn't worry about resizing the table. * * Subclass overrides this to alter the behavior of HashMap(Map), * clone, and readObject. */ void createEntry(int hash, K key, V value, int bucketIndex) { HashMapEntry<K,V> e = table[bucketIndex]; table[bucketIndex] = new HashMapEntry<>(hash, key, value, e); size++; } private abstract class HashIterator<E> implements Iterator<E> { HashMapEntry<K,V> next; // next entry to return int expectedModCount; // For fast-fail int index; // current slot HashMapEntry<K,V> current; // current entry HashIterator() { expectedModCount = modCount; if (size > 0) { // advance to first entry HashMapEntry[] t = table; //找到第一个数组中不为null的键值对 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(); HashMapEntry<K,V> e = next; if (e == null) throw new NoSuchElementException(); //如果e.next不为null,将它设为next,否则遍历数组,直到找到下一个不为null的键值对。 if ((next = e.next) == null) { HashMapEntry[] t = table; while (index < t.length && (next = t[index++]) == null) ; } //前一个next即为current 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的方法删除元素 java.util.HashMap.this.removeEntryForKey(k); //remove后expectedModCount值要更新 expectedModCount = modCount; } } private final class ValueIterator extends java.util.HashMap.HashIterator<V> { //ValueIterator的next方法本质通过nextEntry实现的 public V next() { return nextEntry().getValue(); } } private final class KeyIterator extends java.util.HashMap.HashIterator<K> { //KeyIterator的next方法本质通过nextEntry实现的 public K next() { return nextEntry().getKey(); } } private final class EntryIterator extends java.util.HashMap.HashIterator<Entry<K,V>> { //EntryIterator的next方法本质通过nextEntry实现的 public Entry<K,V> next() { return nextEntry(); } } /* ------------------------------------------------------------ */ // spliterators static class HashMapSpliterator<K,V> { final java.util.HashMap<K,V> map; HashMapEntry<K,V> current; // current entry int index; // current index, modified on advance/split int fence; // one past last index int est; // size estimate int expectedModCount; // for comodification checks HashMapSpliterator(java.util.HashMap<K,V> m, int origin, int fence, int est, int expectedModCount) { this.map = m; this.index = origin; this.fence = fence; this.est = est; this.expectedModCount = expectedModCount; } final int getFence() { // initialize fence and size on first use int hi; if ((hi = fence) < 0) { java.util.HashMap<K,V> m = map; est = m.size; expectedModCount = m.modCount; HashMapEntry<K,V>[] tab = m.table; hi = fence = (tab == null) ? 0 : tab.length; } return hi; } public final long estimateSize() { getFence(); // force init return (long) est; } } static final class KeySpliterator<K,V> extends java.util.HashMap.HashMapSpliterator<K,V> implements Spliterator<K> { KeySpliterator(java.util.HashMap<K,V> m, int origin, int fence, int est, int expectedModCount) { super(m, origin, fence, est, expectedModCount); } public java.util.HashMap.KeySpliterator<K,V> trySplit() { int hi = getFence(), lo = index, mid = (lo + hi) >>> 1; return (lo >= mid || current != null) ? null : new java.util.HashMap.KeySpliterator<>(map, lo, index = mid, est >>>= 1, expectedModCount); } public void forEachRemaining(Consumer<? super K> action) { int i, hi, mc; if (action == null) throw new NullPointerException(); java.util.HashMap<K,V> m = map; HashMapEntry<K,V>[] tab = m.table; if ((hi = fence) < 0) { mc = expectedModCount = m.modCount; hi = fence = (tab == null) ? 0 : tab.length; } else mc = expectedModCount; if (tab != null && tab.length >= hi && (i = index) >= 0 && (i < (index = hi) || current != null)) { HashMapEntry<K,V> p = current; current = null; do { if (p == null) p = tab[i++]; else { action.accept(p.key); p = p.next; } } while (p != null || i < hi); if (m.modCount != mc) throw new ConcurrentModificationException(); } } public boolean tryAdvance(Consumer<? super K> action) { int hi; if (action == null) throw new NullPointerException(); HashMapEntry<K,V>[] tab = map.table; if (tab != null && tab.length >= (hi = getFence()) && index >= 0) { while (current != null || index < hi) { if (current == null) current = tab[index++]; else { K k = current.key; current = current.next; action.accept(k); if (map.modCount != expectedModCount) throw new ConcurrentModificationException(); return true; } } } return false; } public int characteristics() { return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) | Spliterator.DISTINCT | ((map instanceof LinkedHashMap) ? Spliterator.ORDERED : 0); } } static final class ValueSpliterator<K,V> extends java.util.HashMap.HashMapSpliterator<K,V> implements Spliterator<V> { ValueSpliterator(java.util.HashMap<K,V> m, int origin, int fence, int est, int expectedModCount) { super(m, origin, fence, est, expectedModCount); } public java.util.HashMap.ValueSpliterator<K,V> trySplit() { int hi = getFence(), lo = index, mid = (lo + hi) >>> 1; return (lo >= mid || current != null) ? null : new java.util.HashMap.ValueSpliterator<>(map, lo, index = mid, est >>>= 1, expectedModCount); } public void forEachRemaining(Consumer<? super V> action) { int i, hi, mc; if (action == null) throw new NullPointerException(); java.util.HashMap<K,V> m = map; HashMapEntry<K,V>[] tab = m.table; if ((hi = fence) < 0) { mc = expectedModCount = m.modCount; hi = fence = (tab == null) ? 0 : tab.length; } else mc = expectedModCount; if (tab != null && tab.length >= hi && (i = index) >= 0 && (i < (index = hi) || current != null)) { HashMapEntry<K,V> p = current; current = null; do { if (p == null) p = tab[i++]; else { action.accept(p.value); p = p.next; } } while (p != null || i < hi); if (m.modCount != mc) throw new ConcurrentModificationException(); } } public boolean tryAdvance(Consumer<? super V> action) { int hi; if (action == null) throw new NullPointerException(); HashMapEntry<K,V>[] tab = map.table; if (tab != null && tab.length >= (hi = getFence()) && index >= 0) { while (current != null || index < hi) { if (current == null) current = tab[index++]; else { V v = current.value; current = current.next; action.accept(v); if (map.modCount != expectedModCount) throw new ConcurrentModificationException(); return true; } } } return false; } public int characteristics() { return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) | ((map instanceof LinkedHashMap) ? Spliterator.ORDERED : 0); } } static final class EntrySpliterator<K,V> extends java.util.HashMap.HashMapSpliterator<K,V> implements Spliterator<Entry<K,V>> { EntrySpliterator(java.util.HashMap<K,V> m, int origin, int fence, int est, int expectedModCount) { super(m, origin, fence, est, expectedModCount); } public java.util.HashMap.EntrySpliterator<K,V> trySplit() { int hi = getFence(), lo = index, mid = (lo + hi) >>> 1; return (lo >= mid || current != null) ? null : new java.util.HashMap.EntrySpliterator<>(map, lo, index = mid, est >>>= 1, expectedModCount); } public void forEachRemaining(Consumer<? super Entry<K,V>> action) { int i, hi, mc; if (action == null) throw new NullPointerException(); java.util.HashMap<K,V> m = map; HashMapEntry<K,V>[] tab = m.table; if ((hi = fence) < 0) { mc = expectedModCount = m.modCount; hi = fence = (tab == null) ? 0 : tab.length; } else mc = expectedModCount; if (tab != null && tab.length >= hi && (i = index) >= 0 && (i < (index = hi) || current != null)) { HashMapEntry<K,V> p = current; current = null; do { if (p == null) p = tab[i++]; else { action.accept(p); p = p.next; } } while (p != null || i < hi); if (m.modCount != mc) throw new ConcurrentModificationException(); } } public boolean tryAdvance(Consumer<? super Entry<K,V>> action) { int hi; if (action == null) throw new NullPointerException(); HashMapEntry<K,V>[] tab = map.table; if (tab != null && tab.length >= (hi = getFence()) && index >= 0) { while (current != null || index < hi) { if (current == null) current = tab[index++]; else { HashMapEntry<K,V> e = current; current = current.next; action.accept(e); if (map.modCount != expectedModCount) throw new ConcurrentModificationException(); return true; } } } return false; } public int characteristics() { return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) | Spliterator.DISTINCT | ((map instanceof LinkedHashMap) ? Spliterator.ORDERED : 0); } } // Subclass overrides these to alter behavior of views' iterator() method Iterator<K> newKeyIterator() { return new java.util.HashMap.KeyIterator(); } Iterator<V> newValueIterator() { return new java.util.HashMap.ValueIterator(); } Iterator<Entry<K,V>> newEntryIterator() { return new java.util.HashMap.EntryIterator(); } // Views private transient Set<Entry<K,V>> entrySet = null; /** * Returns a {@link Set} view of the keys contained in this map. * The set is backed by the map, so changes to the map are * reflected in the set, and vice-versa. If the map is modified * while an iteration over the set is in progress (except through * the iterator's own <tt>remove</tt> operation), the results of * the iteration are undefined. The set supports element removal, * which removes the corresponding mapping from the map, via the * <tt>Iterator.remove</tt>, <tt>Set.remove</tt>, * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> * operations. It does not support the <tt>add</tt> or <tt>addAll</tt> * operations. */ public Set<K> keySet() { Set<K> ks = keySet; return (ks != null ? ks : (keySet = new java.util.HashMap.KeySet())); } private final class KeySet extends AbstractSet<K> { 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 java.util.HashMap.this.removeEntryForKey(o) != null; } public void clear() { java.util.HashMap.this.clear(); } public final Spliterator<K> spliterator() { return new java.util.HashMap.KeySpliterator<>(java.util.HashMap.this, 0, -1, 0, 0); } public final void forEach(Consumer<? super K> action) { HashMapEntry<K,V>[] tab; if (action == null) throw new NullPointerException(); if (size > 0 && (tab = table) != null) { int mc = modCount; for (int i = 0; i < tab.length; ++i) { for (HashMapEntry<K,V> e = tab[i]; e != null; e = e.next) { action.accept(e.key); // Android-modified - this was outside of the loop, inconsistent with other // collections if (modCount != mc) { throw new ConcurrentModificationException(); } } } } } } /** * Returns a {@link Collection} view of the values contained in this map. * The collection is backed by the map, so changes to the map are * reflected in the collection, and vice-versa. If the map is * modified while an iteration over the collection is in progress * (except through the iterator's own <tt>remove</tt> operation), * the results of the iteration are undefined. The collection * supports element removal, which removes the corresponding * mapping from the map, via the <tt>Iterator.remove</tt>, * <tt>Collection.remove</tt>, <tt>removeAll</tt>, * <tt>retainAll</tt> and <tt>clear</tt> operations. It does not * support the <tt>add</tt> or <tt>addAll</tt> operations. */ public Collection<V> values() { Collection<V> vs = values; return (vs != null ? vs : (values = new java.util.HashMap.Values())); } 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() { java.util.HashMap.this.clear(); } public final Spliterator<V> spliterator() { return new java.util.HashMap.ValueSpliterator<>(java.util.HashMap.this, 0, -1, 0, 0); } public final void forEach(Consumer<? super V> action) { HashMapEntry<K,V>[] tab; if (action == null) throw new NullPointerException(); if (size > 0 && (tab = table) != null) { int mc = modCount; for (int i = 0; i < tab.length; ++i) { for (HashMapEntry<K,V> e = tab[i]; e != null; e = e.next) { action.accept(e.value); // Android-modified - this was outside of the loop, inconsistent with other // collections if (modCount != mc) { throw new ConcurrentModificationException(); } } } } } } /** * Returns a {@link Set} view of the mappings contained in this map. * The set is backed by the map, so changes to the map are * reflected in the set, and vice-versa. If the map is modified * while an iteration over the set is in progress (except through * the iterator's own <tt>remove</tt> operation, or through the * <tt>setValue</tt> operation on a map entry returned by the * iterator) the results of the iteration are undefined. The set * supports element removal, which removes the corresponding * mapping from the map, via the <tt>Iterator.remove</tt>, * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt> and * <tt>clear</tt> operations. It does not support the * <tt>add</tt> or <tt>addAll</tt> operations. * * @return a set view of the mappings contained in this map */ // Android-changed: Changed type parameter from <? extends Entry<K, V> // to a Map.Entry<K, V>. public Set<Entry<K,V>> entrySet() { return entrySet0(); } private Set<Entry<K,V>> entrySet0() { Set<Entry<K,V>> es = entrySet; return es != null ? es : (entrySet = new java.util.HashMap.EntrySet()); } private final class EntrySet extends AbstractSet<Entry<K,V>> { public Iterator<Entry<K,V>> iterator() { return newEntryIterator(); } public boolean contains(Object o) { if (!(o instanceof Map.Entry)) return false; Entry<K,V> e = (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() { java.util.HashMap.this.clear(); } public final Spliterator<Entry<K,V>> spliterator() { return new java.util.HashMap.EntrySpliterator<>(java.util.HashMap.this, 0, -1, 0, 0); } public final void forEach(Consumer<? super Entry<K,V>> action) { HashMapEntry<K,V>[] tab; if (action == null) throw new NullPointerException(); if (size > 0 && (tab = table) != null) { int mc = modCount; for (int i = 0; i < tab.length; ++i) { for (HashMapEntry<K,V> e = tab[i]; e != null; e = e.next) { action.accept(e); // Android-modified - this was outside of the loop, inconsistent with other // collections if (modCount != mc) { throw new ConcurrentModificationException(); } } } } } } @Override public void forEach(BiConsumer<? super K, ? super V> action) { HashMapEntry<K,V>[] tab; if (action == null) throw new NullPointerException(); if (size > 0 && (tab = table) != null) { int mc = modCount; for (int i = 0; i < tab.length; ++i) { for (HashMapEntry<K,V> e = tab[i]; e != null; e = e.next) { action.accept(e.key, e.value); // Android-modified - this was outside of the loop, inconsistent with other // collections if (modCount != mc) { throw new ConcurrentModificationException(); } } } } } @Override public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) { HashMapEntry<K,V>[] tab; if (function == null) throw new NullPointerException(); if (size > 0 && (tab = table) != null) { int mc = modCount; for (int i = 0; i < tab.length; ++i) { for (HashMapEntry<K,V> e = tab[i]; e != null; e = e.next) { e.value = function.apply(e.key, e.value); } } if (modCount != mc) throw new ConcurrentModificationException(); } } /** * Save the state of the <tt>HashMap</tt> instance to a stream (i.e., * serialize it). * * @serialData The <i>capacity</i> of the HashMap (the length of the * bucket array) is emitted (int), followed by the * <i>size</i> (an int, the number of key-value * mappings), followed by the key (Object) and value (Object) * for each key-value mapping. The key-value mappings are * emitted in no particular order. */ private void writeObject(ObjectOutputStream s) throws IOException { // Write out the threshold, loadfactor, and any hidden stuff s.defaultWriteObject(); // Write out number of buckets if (table==EMPTY_TABLE) { s.writeInt(roundUpToPowerOf2(threshold)); } else { s.writeInt(table.length); } // Write out size (number of Mappings) s.writeInt(size); // Write out keys and values (alternating) if (size > 0) { for(Entry<K,V> e : entrySet0()) { s.writeObject(e.getKey()); s.writeObject(e.getValue()); } } } private static final long serialVersionUID = 362498820763181265L; /** * Reconstitute the {@code HashMap} instance from a stream (i.e., * deserialize it). */ private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { // Read in the threshold (ignored), loadfactor, and any hidden stuff s.defaultReadObject(); if (loadFactor <= 0 || Float.isNaN(loadFactor)) { throw new InvalidObjectException("Illegal load factor: " + loadFactor); } // set other fields that need values table = (HashMapEntry<K,V>[]) EMPTY_TABLE; // Read in number of buckets s.readInt(); // ignored. // Read number of mappings int mappings = s.readInt(); if (mappings < 0) throw new InvalidObjectException("Illegal mappings count: " + mappings); // capacity chosen by number of mappings and desired load (if >= 0.25) int capacity = (int) Math.min( mappings * Math.min(1 / loadFactor, 4.0f), // we have limits... java.util.HashMap.MAXIMUM_CAPACITY); // allocate the bucket array; if (mappings > 0) { inflateTable(capacity); } else { threshold = capacity; } init(); // Give subclass a chance to do its thing. // Read the keys and values, and put the mappings in the HashMap for (int i = 0; i < mappings; i++) { K key = (K) s.readObject(); V value = (V) s.readObject(); putForCreate(key, value); } } /** * 重写Map的default方法。 * 替换元素的旧值为新值 * @param key 要替换元素的键 * @param newValue 要替换元素的新值 * @param oldValue 要替换元素的旧值 * @return 替换成功,返回true,反正false */ @Override public boolean replace(K key, V oldValue, V newValue) { HashMapEntry<K,V> e; V v; //查询key键对应的元素,赋值给e,如果e不为null,意味着HashMap有键对应的元素,进入下一步 if ((e = (HashMapEntry)getEntry(key)) != null && //e的value赋值给v,如果v与oldValue引用一致或者判断二者equals为true,说明HashMap中确实有旧元素,可以替换 ((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) { //替换元素的旧值为新值 e.value = newValue; e.recordAccess(this); return true; } return false; } // These methods are used when serializing HashSets int capacity() { return table.length; } float loadFactor() { return loadFactor; } }
转载于:https://my.oschina.net/u/3686484/blog/1558599