WeakHashMap inherits from AbstractMap and implements the Map interface .
Like HashMap, WeakHashMap is also a hash table, and the stored content is also a key-value pair key-value mapping, and both the key and value can be null. The keys of WeakHashMap are all weak keys. Given a key, the existence of its mapping does not prevent the garbage collector from discarding the key, making the key terminable and then recycled. The principle of weak keys is that Entry inherits the WeakReference interface. When GC is recycled, the "weak keys" will also be added to the ReferenceQueuequeue.
Implementation steps:
(1) Create a new WeakHashMap and add key-value pairs to WeakHashMap. WeakHashMap also saves Entries (key-value pairs) through tables. Each Entry is actually A one-way linked list.
(2) When a weak key is no longer referenced by other objects and is recycled by GC, when the GC recycled the weak key, the weak key will also be added to ReferenceQueue(queue) queue.
(3) The next time you need to operate WeakHashMap, the table and queue will be synchronized first. All key-value pairs are stored in the table, and the key-value pairs recycled by GC are stored in the queue. At the same time, the key-value pairs recycled by GC in the table will be deleted. WeakHashMap is also not thread-safe.
Relationship diagram of WeakHashMap:
Variables: table, size, threshold, loadFactor, modCount, queue.
table is an Entry[]size is the size of the Hashtable, which is the number of key-value pairs saved by the Hashtable. threshold is the threshold of Hashtable, used to determine whether the capacity of Hashtable needs to be adjusted. The value of threshold = "capacity * loading factor" loadFactor loading factormodCount is used to implement the fail-fast mechanismqueue saves weak information that has been cleared by GC The referenced key.clear() Object clone() containsKey(Object key) containsValue(Object value) Set<<>> entrySet() V get(Object key) isEmpty() Set<> keySet() V put(K keyV value) putAll(Map<? K? V> map) V remove(Object key) size() Collection<> values()
Traversal method of WeakHashMap
(1) Traverse the key-value pairs of WeakHashMap: first obtain the key-value pair set according to entrySet(), and then traverse the set through the iterator to obtain the key values.Integer integ = Iterator iter = map.entrySet().iterator()(iter.hasNext()) { Map.Entry entry = (Map.Entry)iter.next()key = (String)entry.getKey()integ = (Integer)entry.getValue()}
(2)遍历WeakHashMap的键:首先通过keySet()获取WeakHashMap的键的set集合,通过Iterator迭代器遍历集合来获得键值。
String key = Integer integ = Iterator iter = map.keySet().iterator()(iter.hasNext()) { key = (String)iter.next() integ = (Integer)map.get(key)}
(3)遍历WeakHashMap的值:首先通过values()获取WeakHashMap的value集合,然后对集合进行迭代获得数据。
Integer value = Collection c = map.values()Iterator iter= c.iterator()(iter.hasNext()) { value = (Integer)iter.next()}
WeakHashMap示例程序:
public class Hello { public static void main(String[] args) throws Exception { testWeakHashMapAPIs(); } private static void testWeakHashMapAPIs() { // 初始化3个“弱键” String w1 = new String("one"); String w2 = new String("two"); String w3 = new String("three"); // 新建WeakHashMap Map wmap = new WeakHashMap(); // 添加键值对 wmap.put(w1, "w1"); wmap.put(w2, "w2"); wmap.put(w3, "w3"); // 打印出wmap System.out.printf("\nwmap:%s\n",wmap ); // containsKey(Object key) :是否包含键key System.out.printf("contains key two : %s\n",wmap.containsKey("two")); System.out.printf("contains key five : %s\n",wmap.containsKey("five")); // containsValue(Object value) :是否包含值value System.out.printf("contains value 0 : %s\n",wmap.containsValue(new Integer(0))); // remove(Object key) : 删除键key对应的键值对 wmap.remove("three"); System.out.printf("wmap: %s\n",wmap ); // ---- 测试 WeakHashMap 的自动回收特性 ---- // 将w1设置null。 // 这意味着“弱键”w1再没有被其它对象引用,调用gc时会回收WeakHashMap中与“w1”对应的键值对 w1 = null; // 内存回收。这里,会回收WeakHashMap中与“w1”对应的键值对 System.gc(); // 遍历WeakHashMap Iterator iter = wmap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry en = (Map.Entry)iter.next(); System.out.printf("next : %s - %s\n",en.getKey(),en.getValue()); } // 打印WeakHashMap的实际大小 System.out.printf(" after gc WeakHashMap size:%s\n", wmap.size()); } }
运行结果:
wmap:{three=w3, one=w1, two=w2} contains key two : true contains key five : false contains value 0 : false wmap: {one=w1, two=w2} next : two - w2 after gc WeakHashMap size:1
基于Java8的WeakHashMap源代码:
public class WeakHashMap<K,V> extends AbstractMap<K,V> implements Map<K,V> { private static final int DEFAULT_INITIAL_CAPACITY = 16;//默认初始大小,必须是2的次幂 private static final int MAXIMUM_CAPACITY = 1 << 30;//最大值2的30次方 private static final float DEFAULT_LOAD_FACTOR = 0.75f;//加载因子 Entry<K,V>[] table; private int size;//数目 private int threshold;//阈值 private final float loadFactor;//加载因子 private final ReferenceQueue<Object> queue = new ReferenceQueue<>();//引用队列 int modCount;//fail-fast @SuppressWarnings("unchecked") private Entry<K,V>[] newTable(int n) { return (Entry<K,V>[]) new Entry<?,?>[n]; } //构造函数 public WeakHashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Initial Capacity: "+ initialCapacity); if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal Load factor: "+ loadFactor); int capacity = 1; while (capacity < initialCapacity) capacity <<= 1; table = newTable(capacity); this.loadFactor = loadFactor; threshold = (int)(capacity * loadFactor); } //初始值的构造函数 public WeakHashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } //构造函数 public WeakHashMap() { this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR); } public WeakHashMap(Map<? extends K, ? extends V> m) { this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR); putAll(m); } // internal utilities /** * 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. */ static Object unmaskNull(Object key) { return (key == NULL_KEY) ? null : key; } /** * Checks for equality of non-null reference x and possibly-null y. By * default uses Object.equals. */ private static boolean eq(Object x, Object y) { return x == y || x.equals(y); } /** * Retrieve object hash code and applies a supplemental hash function to the * result hash, which defends against poor quality hash functions. This is * critical because HashMap uses power-of-two length hash tables, that * otherwise encounter collisions for hashCodes that do not differ * in lower bits. */ //计算k的hash final int hash(Object k) { int h = k.hashCode(); // This function ensures that hashCodes that differ only by // constant multiples at each bit position have a bounded // number of collisions (approximately 8 at default load factor). h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); } /** * Returns index for hash code h. */ private static int indexFor(int h, int length) { return h & (length-1); } /** * Expunges stale entries from the table. */ private void expungeStaleEntries() { for (Object x; (x = queue.poll()) != null; ) { synchronized (queue) { @SuppressWarnings("unchecked") Entry<K,V> e = (Entry<K,V>) x; int i = indexFor(e.hash, table.length); Entry<K,V> prev = table[i]; Entry<K,V> p = prev; while (p != null) { Entry<K,V> next = p.next; if (p == e) { if (prev == e) table[i] = next; else prev.next = next; // Must not null out e.next; // stale entries may be in use by a HashIterator e.value = null; // Help GC size--; break; } prev = p; p = next; } } } } /** * Returns the table after first expunging stale entries. */ private Entry<K,V>[] getTable() { expungeStaleEntries(); return table; } /** * Returns the number of key-value mappings in this map. * This result is a snapshot, and may not reflect unprocessed * entries that will be removed before next attempted access * because they are no longer referenced. */ //返回数目 public int size() { if (size == 0) return 0; expungeStaleEntries(); return size; } /** * Returns <tt>true</tt> if this map contains no key-value mappings. * This result is a snapshot, and may not reflect unprocessed * entries that will be removed before next attempted access * because they are no longer referenced. */ //判断是否为空 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) */ //通过key获得value public V get(Object key) { Object k = maskNull(key); int h = hash(k); Entry<K,V>[] tab = getTable(); int index = indexFor(h, tab.length); Entry<K,V> e = tab[index]; while (e != null) { if (e.hash == h && eq(k, e.get())) return e.value; e = e.next; } 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 there is a mapping for <tt>key</tt>; * <tt>false</tt> otherwise */ //判断是否包含某个key public boolean containsKey(Object key) { return getEntry(key) != null; } /** * Returns the entry associated with the specified key in this map. * Returns null if the map contains no mapping for this key. */ //通过key获得entry Entry<K,V> getEntry(Object key) { Object k = maskNull(key); int h = hash(k); Entry<K,V>[] tab = getTable(); int index = indexFor(h, tab.length); Entry<K,V> e = tab[index]; while (e != null && !(e.hash == h && eq(k, e.get()))) e = e.next; return e; } /** * Associates the specified value with the specified key in this map. * If the map previously contained a mapping for this 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>.) */ //插入key和value public V put(K key, V value) { Object k = maskNull(key); int h = hash(k); Entry<K,V>[] tab = getTable(); int i = indexFor(h, tab.length); for (Entry<K,V> e = tab[i]; e != null; e = e.next) { if (h == e.hash && eq(k, e.get())) { V oldValue = e.value; if (value != oldValue) e.value = value; return oldValue; } } modCount++; Entry<K,V> e = tab[i]; tab[i] = new Entry<>(k, value, queue, h, e); if (++size >= threshold) resize(tab.length * 2); return null; } /** * 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) { Entry<K,V>[] oldTable = getTable(); int oldCapacity = oldTable.length; if (oldCapacity == MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return; } Entry<K,V>[] newTable = newTable(newCapacity); transfer(oldTable, newTable); table = newTable; /* * If ignoring null elements and processing ref queue caused massive * shrinkage, then restore old table. This should be rare, but avoids * unbounded expansion of garbage-filled tables. */ if (size >= threshold / 2) { threshold = (int)(newCapacity * loadFactor); } else { expungeStaleEntries(); transfer(newTable, oldTable); table = oldTable; } } /** Transfers all entries from src to dest tables */ private void transfer(Entry<K,V>[] src, Entry<K,V>[] dest) { for (int j = 0; j < src.length; ++j) { Entry<K,V> e = src[j]; src[j] = null; while (e != null) { Entry<K,V> next = e.next; Object key = e.get(); if (key == null) { e.next = null; // Help GC e.value = null; // " " size--; } else { int i = indexFor(e.hash, dest.length); e.next = dest[i]; dest[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; /* * 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 (Map.Entry<? extends K, ? extends V> e : m.entrySet()) put(e.getKey(), e.getValue()); } /** * Removes the mapping for a key from this weak hash map if it is present. * More formally, if this map contains a mapping from key <tt>k</tt> to * value <tt>v</tt> such that <code>(key==null ? k==null : * key.equals(k))</code>, that mapping is removed. (The map can contain * at most one such mapping.) * * <p>Returns the value to which this map previously associated the key, * or <tt>null</tt> if the map contained no mapping for the key. A * return value of <tt>null</tt> does not <i>necessarily</i> indicate * that the map contained no mapping for the key; it's also possible * that the map explicitly mapped the key to <tt>null</tt>. * * <p>The map will not contain a mapping for the specified key once the * call returns. * * @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> */ //根据key删除 public V remove(Object key) { Object k = maskNull(key); int h = hash(k); Entry<K,V>[] tab = getTable(); int i = indexFor(h, tab.length); Entry<K,V> prev = tab[i]; Entry<K,V> e = prev; while (e != null) { Entry<K,V> next = e.next; if (h == e.hash && eq(k, e.get())) { modCount++; size--; if (prev == e) tab[i] = next; else prev.next = next; return e.value; } prev = e; e = next; } return null; } /** Special version of remove needed by Entry set */ boolean removeMapping(Object o) { if (!(o instanceof Map.Entry)) return false; Entry<K,V>[] tab = getTable(); Map.Entry<?,?> entry = (Map.Entry<?,?>)o; Object k = maskNull(entry.getKey()); int h = hash(k); int i = indexFor(h, tab.length); Entry<K,V> prev = tab[i]; Entry<K,V> e = prev; while (e != null) { Entry<K,V> next = e.next; if (h == e.hash && e.equals(entry)) { modCount++; size--; if (prev == e) tab[i] = next; else prev.next = next; return true; } prev = e; e = next; } return false; } /** * Removes all of the mappings from this map. * The map will be empty after this call returns. */ //清空 public void clear() { // clear out ref queue. We don't need to expunge entries // since table is getting cleared. while (queue.poll() != null) ; modCount++; Arrays.fill(table, null); size = 0; // Allocation of array may have caused GC, which may have caused // additional entries to go stale. Removing these entries from the // reference queue will make them eligible for reclamation. while (queue.poll() != null) ; } /** * 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(); Entry<K,V>[] tab = getTable(); for (int i = tab.length; i-- > 0;) for (Entry<K,V> 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() { Entry<K,V>[] tab = getTable(); for (int i = tab.length; i-- > 0;) for (Entry<K,V> e = tab[i]; e != null; e = e.next) if (e.value==null) return true; return false; } /** * The entries in this hash table extend WeakReference, using its main ref * field as the key. */ //entry继承了虚引用 private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> { V value; final int hash; Entry<K,V> next; /** * Creates new entry. */ Entry(Object key, V value, ReferenceQueue<Object> queue, int hash, Entry<K,V> next) { super(key, queue); this.value = value; this.hash = hash; this.next = next; } @SuppressWarnings("unchecked") public K getKey() { return (K) WeakHashMap.unmaskNull(get()); } public V getValue() { return value; } public V setValue(V newValue) { V oldValue = value; value = newValue; return oldValue; } public boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry<?,?> e = (Map.Entry<?,?>)o; K k1 = getKey(); Object k2 = e.getKey(); if (k1 == k2 || (k1 != null && k1.equals(k2))) { V v1 = getValue(); Object v2 = e.getValue(); if (v1 == v2 || (v1 != null && v1.equals(v2))) return true; } return false; } public int hashCode() { K k = getKey(); V v = getValue(); return Objects.hashCode(k) ^ Objects.hashCode(v); } public String toString() { return getKey() + "=" + getValue(); } } private abstract class HashIterator<T> implements Iterator<T> { private int index; private Entry<K,V> entry; private Entry<K,V> lastReturned; private int expectedModCount = modCount; /** * Strong reference needed to avoid disappearance of key * between hasNext and next */ private Object nextKey; /** * Strong reference needed to avoid disappearance of key * between nextEntry() and any use of the entry */ private Object currentKey; HashIterator() { index = isEmpty() ? 0 : table.length; } public boolean hasNext() { Entry<K,V>[] t = table; while (nextKey == null) { Entry<K,V> e = entry; int i = index; while (e == null && i > 0) e = t[--i]; entry = e; index = i; if (e == null) { currentKey = null; return false; } nextKey = e.get(); // hold on to key in strong ref if (nextKey == null) entry = entry.next; } return true; } /** The common parts of next() across different types of iterators */ protected Entry<K,V> nextEntry() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); if (nextKey == null && !hasNext()) throw new NoSuchElementException(); lastReturned = entry; entry = entry.next; currentKey = nextKey; nextKey = null; return lastReturned; } public void remove() { if (lastReturned == null) throw new IllegalStateException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); WeakHashMap.this.remove(currentKey); expectedModCount = modCount; lastReturned = null; currentKey = null; } } private class ValueIterator extends HashIterator<V> { public V next() { return nextEntry().value; } } private class KeyIterator extends HashIterator<K> { public K next() { return nextEntry().getKey(); } } private class EntryIterator extends HashIterator<Map.Entry<K,V>> { public Map.Entry<K,V> next() { return nextEntry(); } } // Views private transient Set<Map.Entry<K,V>> entrySet; /** * 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 KeySet())); } private class KeySet extends AbstractSet<K> { public Iterator<K> iterator() { return new KeyIterator(); } public int size() { return WeakHashMap.this.size(); } public boolean contains(Object o) { return containsKey(o); } public boolean remove(Object o) { if (containsKey(o)) { WeakHashMap.this.remove(o); return true; } else return false; } public void clear() { WeakHashMap.this.clear(); } public Spliterator<K> spliterator() { return new KeySpliterator<>(WeakHashMap.this, 0, -1, 0, 0); } } /** * 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 Values()); } private class Values extends AbstractCollection<V> { public Iterator<V> iterator() { return new ValueIterator(); } public int size() { return WeakHashMap.this.size(); } public boolean contains(Object o) { return containsValue(o); } public void clear() { WeakHashMap.this.clear(); } public Spliterator<V> spliterator() { return new ValueSpliterator<>(WeakHashMap.this, 0, -1, 0, 0); } } /** * 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. */ public Set<Map.Entry<K,V>> entrySet() { Set<Map.Entry<K,V>> es = entrySet; return es != null ? es : (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<?,?> e = (Map.Entry<?,?>)o; Entry<K,V> candidate = getEntry(e.getKey()); return candidate != null && candidate.equals(e); } public boolean remove(Object o) { return removeMapping(o); } public int size() { return WeakHashMap.this.size(); } public void clear() { WeakHashMap.this.clear(); } private List<Map.Entry<K,V>> deepCopy() { List<Map.Entry<K,V>> list = new ArrayList<>(size()); for (Map.Entry<K,V> e : this) list.add(new AbstractMap.SimpleEntry<>(e)); return list; } public Object[] toArray() { return deepCopy().toArray(); } public <T> T[] toArray(T[] a) { return deepCopy().toArray(a); } public Spliterator<Map.Entry<K,V>> spliterator() { return new EntrySpliterator<>(WeakHashMap.this, 0, -1, 0, 0); } } @SuppressWarnings("unchecked") @Override public void forEach(BiConsumer<? super K, ? super V> action) { Objects.requireNonNull(action); int expectedModCount = modCount; Entry<K, V>[] tab = getTable(); for (Entry<K, V> entry : tab) { while (entry != null) { Object key = entry.get(); if (key != null) { action.accept((K)WeakHashMap.unmaskNull(key), entry.value); } entry = entry.next; if (expectedModCount != modCount) { throw new ConcurrentModificationException(); } } } } @SuppressWarnings("unchecked") @Override public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) { Objects.requireNonNull(function); int expectedModCount = modCount; Entry<K, V>[] tab = getTable();; for (Entry<K, V> entry : tab) { while (entry != null) { Object key = entry.get(); if (key != null) { entry.value = function.apply((K)WeakHashMap.unmaskNull(key), entry.value); } entry = entry.next; if (expectedModCount != modCount) { throw new ConcurrentModificationException(); } } } } /** * Similar form as other hash Spliterators, but skips dead * elements. */ static class WeakHashMapSpliterator<K,V> { final WeakHashMap<K,V> map; WeakHashMap.Entry<K,V> current; // current node int index; // current index, modified on advance/split int fence; // -1 until first use; then one past last index int est; // size estimate int expectedModCount; // for comodification checks WeakHashMapSpliterator(WeakHashMap<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) { WeakHashMap<K,V> m = map; est = m.size(); expectedModCount = m.modCount; hi = fence = m.table.length; } return hi; } public final long estimateSize() { getFence(); // force init return (long) est; } } static final class KeySpliterator<K,V> extends WeakHashMapSpliterator<K,V> implements Spliterator<K> { KeySpliterator(WeakHashMap<K,V> m, int origin, int fence, int est, int expectedModCount) { super(m, origin, fence, est, expectedModCount); } public KeySpliterator<K,V> trySplit() { int hi = getFence(), lo = index, mid = (lo + hi) >>> 1; return (lo >= mid) ? null : new KeySpliterator<K,V>(map, lo, index = mid, est >>>= 1, expectedModCount); } public void forEachRemaining(Consumer<? super K> action) { int i, hi, mc; if (action == null) throw new NullPointerException(); WeakHashMap<K,V> m = map; WeakHashMap.Entry<K,V>[] tab = m.table; if ((hi = fence) < 0) { mc = expectedModCount = m.modCount; hi = fence = tab.length; } else mc = expectedModCount; if (tab.length >= hi && (i = index) >= 0 && (i < (index = hi) || current != null)) { WeakHashMap.Entry<K,V> p = current; current = null; // exhaust do { if (p == null) p = tab[i++]; else { Object x = p.get(); p = p.next; if (x != null) { @SuppressWarnings("unchecked") K k = (K) WeakHashMap.unmaskNull(x); action.accept(k); } } } 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(); WeakHashMap.Entry<K,V>[] tab = map.table; if (tab.length >= (hi = getFence()) && index >= 0) { while (current != null || index < hi) { if (current == null) current = tab[index++]; else { Object x = current.get(); current = current.next; if (x != null) { @SuppressWarnings("unchecked") K k = (K) WeakHashMap.unmaskNull(x); action.accept(k); if (map.modCount != expectedModCount) throw new ConcurrentModificationException(); return true; } } } } return false; } public int characteristics() { return Spliterator.DISTINCT; } } static final class ValueSpliterator<K,V> extends WeakHashMapSpliterator<K,V> implements Spliterator<V> { ValueSpliterator(WeakHashMap<K,V> m, int origin, int fence, int est, int expectedModCount) { super(m, origin, fence, est, expectedModCount); } public ValueSpliterator<K,V> trySplit() { int hi = getFence(), lo = index, mid = (lo + hi) >>> 1; return (lo >= mid) ? null : new ValueSpliterator<K,V>(map, lo, index = mid, est >>>= 1, expectedModCount); } public void forEachRemaining(Consumer<? super V> action) { int i, hi, mc; if (action == null) throw new NullPointerException(); WeakHashMap<K,V> m = map; WeakHashMap.Entry<K,V>[] tab = m.table; if ((hi = fence) < 0) { mc = expectedModCount = m.modCount; hi = fence = tab.length; } else mc = expectedModCount; if (tab.length >= hi && (i = index) >= 0 && (i < (index = hi) || current != null)) { WeakHashMap.Entry<K,V> p = current; current = null; // exhaust do { if (p == null) p = tab[i++]; else { Object x = p.get(); V v = p.value; p = p.next; if (x != null) action.accept(v); } } 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(); WeakHashMap.Entry<K,V>[] tab = map.table; if (tab.length >= (hi = getFence()) && index >= 0) { while (current != null || index < hi) { if (current == null) current = tab[index++]; else { Object x = current.get(); V v = current.value; current = current.next; if (x != null) { action.accept(v); if (map.modCount != expectedModCount) throw new ConcurrentModificationException(); return true; } } } } return false; } public int characteristics() { return 0; } } static final class EntrySpliterator<K,V> extends WeakHashMapSpliterator<K,V> implements Spliterator<Map.Entry<K,V>> { EntrySpliterator(WeakHashMap<K,V> m, int origin, int fence, int est, int expectedModCount) { super(m, origin, fence, est, expectedModCount); } public EntrySpliterator<K,V> trySplit() { int hi = getFence(), lo = index, mid = (lo + hi) >>> 1; return (lo >= mid) ? null : new EntrySpliterator<K,V>(map, lo, index = mid, est >>>= 1, expectedModCount); } public void forEachRemaining(Consumer<? super Map.Entry<K, V>> action) { int i, hi, mc; if (action == null) throw new NullPointerException(); WeakHashMap<K,V> m = map; WeakHashMap.Entry<K,V>[] tab = m.table; if ((hi = fence) < 0) { mc = expectedModCount = m.modCount; hi = fence = tab.length; } else mc = expectedModCount; if (tab.length >= hi && (i = index) >= 0 && (i < (index = hi) || current != null)) { WeakHashMap.Entry<K,V> p = current; current = null; // exhaust do { if (p == null) p = tab[i++]; else { Object x = p.get(); V v = p.value; p = p.next; if (x != null) { @SuppressWarnings("unchecked") K k = (K) WeakHashMap.unmaskNull(x); action.accept (new AbstractMap.SimpleImmutableEntry<K,V>(k, v)); } } } while (p != null || i < hi); } if (m.modCount != mc) throw new ConcurrentModificationException(); } public boolean tryAdvance(Consumer<? super Map.Entry<K,V>> action) { int hi; if (action == null) throw new NullPointerException(); WeakHashMap.Entry<K,V>[] tab = map.table; if (tab.length >= (hi = getFence()) && index >= 0) { while (current != null || index < hi) { if (current == null) current = tab[index++]; else { Object x = current.get(); V v = current.value; current = current.next; if (x != null) { @SuppressWarnings("unchecked") K k = (K) WeakHashMap.unmaskNull(x); action.accept (new AbstractMap.SimpleImmutableEntry<K,V>(k, v)); if (map.modCount != expectedModCount) throw new ConcurrentModificationException(); return true; } } } } return false; } public int characteristics() { return Spliterator.DISTINCT; } } }
The above is the detailed content of Detailed explanation of WeakHashMap in Java collections. For more information, please follow other related articles on the PHP Chinese website!