package java.util; import java.lang.ref.WeakReference; import java.lang.ref.ReferenceQueue; import java.util.concurrent.ThreadLocalRandom; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Consumer; /** * 基于Map接口的弱关键字(weak keys)散列表实现。存储的内容也是键值对(key-value)映射,而且键和值都可以是null。 * 当某个关键字不再被引用的时候,WeakHashMap会自动删除该关键字对应的Entry。 * 对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,并被终止,然后被回收。 * 某个键被终止时,它对应的键值对也就从映射中有效移除了。所以这个类的表现行为不同于其他Map接口的实现类。 * WeakHashMap类与HashMap类有类似的字符串性能,同样性能由两个参数影响:初始容量initial capacity和装载因子load factor。 * * 和HashMap一样,WeakHashMap是不同步的。可以使用Collections.synchronizedMap方法来构造同步的WeakHashMap。 * * WeakHashMap类最初是用==操作符来调用Key对象的equals函数测试对象相等。 * WeakHashMap对于equals方法不是基于对象相等的关键字对象也能很好的工作,比如String实例。 * 然而,对于这样可重新创建的关键字对象,从WeakHashMap中移除被丢弃的关键字对应的Entry这样的原子操作可能会产生混淆。 * WeakHashMap的行为多少会依赖垃圾收集器的行为,所以一些常用的Map约束并不适用于这个类。因为垃圾收集器会随时丢弃关键字。 * 一个WeakHashMap实例可能表现的好像有一个未知的线程安静地删除Entry。即使是同步synchronized修饰后,也还是会随着时间删除关键字。 * * WeakHashMap中的关键字对象都是弱键的引用对象。当一个关键字只被弱引用时,不论是Map内部还是外部的弱引用,都会被垃圾收集器自动移除。 * 注意:WeakHashMap的值对象是普通的强引用。要注意不要让值对象强引用到自身的关键字上,直接或间接都不行,这样会阻止关键字的回收。 * 值引用可能会间接引用到WeakHashMap本身的关键字,所以在添加键值对的方式最好是:m.put(key, new WeakReference(value)) * 然后每次get操作之后再拆解掉WeakReference。 * * iterator返回的迭代器是fail-fast的。 */ /** * WeakHashMap继承于AbstractMap,实现了Map接口。WeakHashMap的键是弱键。 * 弱键的原理是通过WeakReference和ReferenceQueue实现的。WeakHashMap的Key是WeakReference类型的。 * ReferenceQueue是一个队列,它会保存被GC回收的弱键。实现步骤是: * 1)新建WeakHashMap,将键值对添加到WeakHashMap中。实际上,WeakHashMap是通过数组table保存Entry,每个Entry是一个单向键值对链表。 * 2)当某弱键不再被其他对象引用,并被GC回收时,在GC回收该弱键时,这个弱键也同时会被添加到ReferenceQueue(Queue)队列中。 * 3)当下一次需要操作WeakHashMap时,会先同步table和Queue。table中保存了全部的键值对,而Queue中保存了被GC回收的键值对;同步它们,就是删除table中被GC回收的键值对。 * 这就是弱键如何被自动从WeakHashMap中删除的步骤了。 */ public class WeakHashMap<K,V> extends AbstractMap<K,V> implements Map<K,V> { /** * 默认初始容量 -- 必须是2的次幂 */ private static final int DEFAULT_INITIAL_CAPACITY = 16; /** * 最大容量,必须是小于1<<30的2的次幂 */ private static final int MAXIMUM_CAPACITY = 1 << 30; /** * 默认装载因子 */ private static final float DEFAULT_LOAD_FACTOR = 0.75f; /** * table数组,必要时会改变大小,但长度必须是2的次幂 */ Entry<K,V>[] table; /** * WeakHashMap中键值对的数目 */ private int size; /** * 更改大小时下一个数组大小(容量*装载因子) */ private int threshold; /** * 哈希表的装载因子 */ private final float loadFactor; /** * 已经被GC清除的弱引用键值对(WeakEntry)的引用队列 */ private final ReferenceQueue<Object> queue = new ReferenceQueue<>(); /** * WeakHashMap的结构更改次数。用于fail-fast */ int modCount; //创建Entry数组table @SuppressWarnings("unchecked") private Entry<K,V>[] newTable(int n) { return (Entry<K,V>[]) new Entry<?,?>[n]; } /** * 构造函数1:指定容量大小和加载因子的构造函数 */ 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); } /** * 构造函数2:指定容量大小构造函数,默认装载因子 * @param initialCapacity The initial capacity of the <tt>WeakHashMap</tt> * @throws IllegalArgumentException if the initial capacity is negative */ public WeakHashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } /** * 构造函数3:默认容量和默认装载因子 */ public WeakHashMap() { this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR); } /** * 构造函数4:根据指定Map中的键值对构造WeakHashMap,默认装载因子,初始容量为能容纳Map中关键字为准 * @param m the map whose mappings are to be placed in this map * @throws NullPointerException if the specified map is null */ 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); } // 内部工具函数 /** * 用于table内部表示空关键字的值对象NULL_KEY */ private static final Object NULL_KEY = new Object(); /** * 如果关键字为null,使用NULL_KEY替换 */ private static Object maskNull(Object key) { return (key == null) ? NULL_KEY : key; } /** * 将空关键字的内部表示转换为null */ static Object unmaskNull(Object key) { return (key == NULL_KEY) ? null : key; } /** * 验证非空引用x和可能为空的引用y之间的相等性,默认使用equals方法 */ private static boolean eq(Object x, Object y) { return x == y || x.equals(y); } /** * 返回对象的哈希值 */ 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); } /** * 指定哈希值的下标 */ private static int indexFor(int h, int length) { return h & (length-1); } /** * 从数组table中移除被丢弃的Entry * WeakHashMap进行增删改查时,都调用了expungeStaleEntries()方法 */ private void expungeStaleEntries() { for (Object x; (x = queue.poll()) != null; ) { synchronized (queue) { //e为要清理的对象 @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循环遍历冲突链表 while (p != null) { Entry<K,V> next = p.next; if (p == e) { //如果e为链表第一个Entry 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 //这里把value赋值为null,来帮助GC回收强引用的value e.value = null; //help GC size--; break; } prev = p; p = next; } } } } /** * 先移除丢弃的Entry,然后返回数组table */ private Entry<K,V>[] getTable() { expungeStaleEntries(); return table; } /** * 返回Map中键值对的数目。返回之前先调用expungeStaleEntries()方法 */ public int size() { if (size == 0) return 0; expungeStaleEntries(); return size; } /** * Map中不包含任何键值对时返回true */ public boolean isEmpty() { return size() == 0; } /** * 返回指定关键字的value值,不存在则返回null。 */ public V get(Object key) { Object k = maskNull(key); int h = hash(k); //getTable中调用了expungeStaleEntries()方法 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()))//关键字的哈希值相同且值对象相等(eq方法返回true) 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 */ 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. */ Entry<K,V> getEntry(Object key) { Object k = maskNull(key); int h = hash(k); //getTable方法中调用了expungeStaleEntries()清除掉丢弃的Entry 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; } /** * 将指定值和指定关键字关联起来 * @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) { Object k = maskNull(key); int h = hash(k); //getTable中调用了expungeStaleEntries()方法,更新了table数组 Entry<K,V>[] tab = getTable(); int i = indexFor(h, tab.length); //遍历冲突链,若找到相同Entry,则替换旧值 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; } } //若没有找到相同Entry,在链表开头插入新的Entry 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; } /** * 将Map中的内容重新哈希到新的更大容量的数组中 * 如果当前容量是MAXIMUM_CAPACITY,resize方法不会重新哈希,而是把阈值设置为Integer.MAX_VALUE * 可以防止之后再调用这个方法 * @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) { //先调用expungeStaleEntries()删除已被GC清除的弱引用对象 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; } } /** 将src中所有的Entry移动到dest数组中*/ 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()); } /** * 如果存在,删除指定关键字对应的Entry对象 * @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> */ 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) ; } /** * 如果Map中包含指定value值的Entry,返回true * @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; } /** * 是否包含空值,true为包含 */ 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; } /** * 哈希表中的Entry继承自WeakReference,使用ref字段作为关键字 * WeakHashMap.Entry<K,V>中并没有保存Key,只是将Key与ReferenceQueue关联上了。 */ private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> { V value; final int hash; Entry<K,V> next; /** * 创建新的Entry */ Entry(Object key, V value, ReferenceQueue<Object> queue, int hash, Entry<K,V> next) { super(key, queue); //指定当前Entry对象的ReferenceQueue 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 = null; private Entry<K,V> lastReturned = null; private int expectedModCount = modCount; /** * 为了防止hasNext和next操作之间有关键字消失,这里nextKey是强引用 */ private Object nextKey = null; /** * 强引用,防止在nextEntry和Entry的其他使用阶段发生关键字消失 */ private Object currentKey = null; 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 = 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 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(); } } } } /** * 和其他哈希Spliterators类似,只是跳过了已删除元素 */ 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; } } }