1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/emul/compact/src/main/java/java/util/WeakHashMap.java Sat Sep 07 13:51:24 2013 +0200
1.3 @@ -0,0 +1,972 @@
1.4 +/*
1.5 + * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
1.7 + *
1.8 + * This code is free software; you can redistribute it and/or modify it
1.9 + * under the terms of the GNU General Public License version 2 only, as
1.10 + * published by the Free Software Foundation. Oracle designates this
1.11 + * particular file as subject to the "Classpath" exception as provided
1.12 + * by Oracle in the LICENSE file that accompanied this code.
1.13 + *
1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
1.17 + * version 2 for more details (a copy is included in the LICENSE file that
1.18 + * accompanied this code).
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License version
1.21 + * 2 along with this work; if not, write to the Free Software Foundation,
1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
1.23 + *
1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
1.25 + * or visit www.oracle.com if you need additional information or have any
1.26 + * questions.
1.27 + */
1.28 +
1.29 +package java.util;
1.30 +import java.lang.ref.WeakReference;
1.31 +import java.lang.ref.ReferenceQueue;
1.32 +
1.33 +
1.34 +/**
1.35 + * Hash table based implementation of the <tt>Map</tt> interface, with
1.36 + * <em>weak keys</em>.
1.37 + * An entry in a <tt>WeakHashMap</tt> will automatically be removed when
1.38 + * its key is no longer in ordinary use. More precisely, the presence of a
1.39 + * mapping for a given key will not prevent the key from being discarded by the
1.40 + * garbage collector, that is, made finalizable, finalized, and then reclaimed.
1.41 + * When a key has been discarded its entry is effectively removed from the map,
1.42 + * so this class behaves somewhat differently from other <tt>Map</tt>
1.43 + * implementations.
1.44 + *
1.45 + * <p> Both null values and the null key are supported. This class has
1.46 + * performance characteristics similar to those of the <tt>HashMap</tt>
1.47 + * class, and has the same efficiency parameters of <em>initial capacity</em>
1.48 + * and <em>load factor</em>.
1.49 + *
1.50 + * <p> Like most collection classes, this class is not synchronized.
1.51 + * A synchronized <tt>WeakHashMap</tt> may be constructed using the
1.52 + * {@link Collections#synchronizedMap Collections.synchronizedMap}
1.53 + * method.
1.54 + *
1.55 + * <p> This class is intended primarily for use with key objects whose
1.56 + * <tt>equals</tt> methods test for object identity using the
1.57 + * <tt>==</tt> operator. Once such a key is discarded it can never be
1.58 + * recreated, so it is impossible to do a lookup of that key in a
1.59 + * <tt>WeakHashMap</tt> at some later time and be surprised that its entry
1.60 + * has been removed. This class will work perfectly well with key objects
1.61 + * whose <tt>equals</tt> methods are not based upon object identity, such
1.62 + * as <tt>String</tt> instances. With such recreatable key objects,
1.63 + * however, the automatic removal of <tt>WeakHashMap</tt> entries whose
1.64 + * keys have been discarded may prove to be confusing.
1.65 + *
1.66 + * <p> The behavior of the <tt>WeakHashMap</tt> class depends in part upon
1.67 + * the actions of the garbage collector, so several familiar (though not
1.68 + * required) <tt>Map</tt> invariants do not hold for this class. Because
1.69 + * the garbage collector may discard keys at any time, a
1.70 + * <tt>WeakHashMap</tt> may behave as though an unknown thread is silently
1.71 + * removing entries. In particular, even if you synchronize on a
1.72 + * <tt>WeakHashMap</tt> instance and invoke none of its mutator methods, it
1.73 + * is possible for the <tt>size</tt> method to return smaller values over
1.74 + * time, for the <tt>isEmpty</tt> method to return <tt>false</tt> and
1.75 + * then <tt>true</tt>, for the <tt>containsKey</tt> method to return
1.76 + * <tt>true</tt> and later <tt>false</tt> for a given key, for the
1.77 + * <tt>get</tt> method to return a value for a given key but later return
1.78 + * <tt>null</tt>, for the <tt>put</tt> method to return
1.79 + * <tt>null</tt> and the <tt>remove</tt> method to return
1.80 + * <tt>false</tt> for a key that previously appeared to be in the map, and
1.81 + * for successive examinations of the key set, the value collection, and
1.82 + * the entry set to yield successively smaller numbers of elements.
1.83 + *
1.84 + * <p> Each key object in a <tt>WeakHashMap</tt> is stored indirectly as
1.85 + * the referent of a weak reference. Therefore a key will automatically be
1.86 + * removed only after the weak references to it, both inside and outside of the
1.87 + * map, have been cleared by the garbage collector.
1.88 + *
1.89 + * <p> <strong>Implementation note:</strong> The value objects in a
1.90 + * <tt>WeakHashMap</tt> are held by ordinary strong references. Thus care
1.91 + * should be taken to ensure that value objects do not strongly refer to their
1.92 + * own keys, either directly or indirectly, since that will prevent the keys
1.93 + * from being discarded. Note that a value object may refer indirectly to its
1.94 + * key via the <tt>WeakHashMap</tt> itself; that is, a value object may
1.95 + * strongly refer to some other key object whose associated value object, in
1.96 + * turn, strongly refers to the key of the first value object. One way
1.97 + * to deal with this is to wrap values themselves within
1.98 + * <tt>WeakReferences</tt> before
1.99 + * inserting, as in: <tt>m.put(key, new WeakReference(value))</tt>,
1.100 + * and then unwrapping upon each <tt>get</tt>.
1.101 + *
1.102 + * <p>The iterators returned by the <tt>iterator</tt> method of the collections
1.103 + * returned by all of this class's "collection view methods" are
1.104 + * <i>fail-fast</i>: if the map is structurally modified at any time after the
1.105 + * iterator is created, in any way except through the iterator's own
1.106 + * <tt>remove</tt> method, the iterator will throw a {@link
1.107 + * ConcurrentModificationException}. Thus, in the face of concurrent
1.108 + * modification, the iterator fails quickly and cleanly, rather than risking
1.109 + * arbitrary, non-deterministic behavior at an undetermined time in the future.
1.110 + *
1.111 + * <p>Note that the fail-fast behavior of an iterator cannot be guaranteed
1.112 + * as it is, generally speaking, impossible to make any hard guarantees in the
1.113 + * presence of unsynchronized concurrent modification. Fail-fast iterators
1.114 + * throw <tt>ConcurrentModificationException</tt> on a best-effort basis.
1.115 + * Therefore, it would be wrong to write a program that depended on this
1.116 + * exception for its correctness: <i>the fail-fast behavior of iterators
1.117 + * should be used only to detect bugs.</i>
1.118 + *
1.119 + * <p>This class is a member of the
1.120 + * <a href="{@docRoot}/../technotes/guides/collections/index.html">
1.121 + * Java Collections Framework</a>.
1.122 + *
1.123 + * @param <K> the type of keys maintained by this map
1.124 + * @param <V> the type of mapped values
1.125 + *
1.126 + * @author Doug Lea
1.127 + * @author Josh Bloch
1.128 + * @author Mark Reinhold
1.129 + * @since 1.2
1.130 + * @see java.util.HashMap
1.131 + * @see java.lang.ref.WeakReference
1.132 + */
1.133 +public class WeakHashMap<K,V>
1.134 + extends AbstractMap<K,V>
1.135 + implements Map<K,V> {
1.136 +
1.137 + /**
1.138 + * The default initial capacity -- MUST be a power of two.
1.139 + */
1.140 + private static final int DEFAULT_INITIAL_CAPACITY = 16;
1.141 +
1.142 + /**
1.143 + * The maximum capacity, used if a higher value is implicitly specified
1.144 + * by either of the constructors with arguments.
1.145 + * MUST be a power of two <= 1<<30.
1.146 + */
1.147 + private static final int MAXIMUM_CAPACITY = 1 << 30;
1.148 +
1.149 + /**
1.150 + * The load factor used when none specified in constructor.
1.151 + */
1.152 + private static final float DEFAULT_LOAD_FACTOR = 0.75f;
1.153 +
1.154 + /**
1.155 + * The table, resized as necessary. Length MUST Always be a power of two.
1.156 + */
1.157 + Entry<K,V>[] table;
1.158 +
1.159 + /**
1.160 + * The number of key-value mappings contained in this weak hash map.
1.161 + */
1.162 + private int size;
1.163 +
1.164 + /**
1.165 + * The next size value at which to resize (capacity * load factor).
1.166 + */
1.167 + private int threshold;
1.168 +
1.169 + /**
1.170 + * The load factor for the hash table.
1.171 + */
1.172 + private final float loadFactor;
1.173 +
1.174 + /**
1.175 + * Reference queue for cleared WeakEntries
1.176 + */
1.177 + private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
1.178 +
1.179 + /**
1.180 + * The number of times this WeakHashMap has been structurally modified.
1.181 + * Structural modifications are those that change the number of
1.182 + * mappings in the map or otherwise modify its internal structure
1.183 + * (e.g., rehash). This field is used to make iterators on
1.184 + * Collection-views of the map fail-fast.
1.185 + *
1.186 + * @see ConcurrentModificationException
1.187 + */
1.188 + int modCount;
1.189 +
1.190 + @SuppressWarnings("unchecked")
1.191 + private Entry<K,V>[] newTable(int n) {
1.192 + return (Entry<K,V>[]) new Entry[n];
1.193 + }
1.194 +
1.195 + /**
1.196 + * Constructs a new, empty <tt>WeakHashMap</tt> with the given initial
1.197 + * capacity and the given load factor.
1.198 + *
1.199 + * @param initialCapacity The initial capacity of the <tt>WeakHashMap</tt>
1.200 + * @param loadFactor The load factor of the <tt>WeakHashMap</tt>
1.201 + * @throws IllegalArgumentException if the initial capacity is negative,
1.202 + * or if the load factor is nonpositive.
1.203 + */
1.204 + public WeakHashMap(int initialCapacity, float loadFactor) {
1.205 + if (initialCapacity < 0)
1.206 + throw new IllegalArgumentException("Illegal Initial Capacity: "+
1.207 + initialCapacity);
1.208 + if (initialCapacity > MAXIMUM_CAPACITY)
1.209 + initialCapacity = MAXIMUM_CAPACITY;
1.210 +
1.211 + if (loadFactor <= 0 || Float.isNaN(loadFactor))
1.212 + throw new IllegalArgumentException("Illegal Load factor: "+
1.213 + loadFactor);
1.214 + int capacity = 1;
1.215 + while (capacity < initialCapacity)
1.216 + capacity <<= 1;
1.217 + table = newTable(capacity);
1.218 + this.loadFactor = loadFactor;
1.219 + threshold = (int)(capacity * loadFactor);
1.220 + }
1.221 +
1.222 + /**
1.223 + * Constructs a new, empty <tt>WeakHashMap</tt> with the given initial
1.224 + * capacity and the default load factor (0.75).
1.225 + *
1.226 + * @param initialCapacity The initial capacity of the <tt>WeakHashMap</tt>
1.227 + * @throws IllegalArgumentException if the initial capacity is negative
1.228 + */
1.229 + public WeakHashMap(int initialCapacity) {
1.230 + this(initialCapacity, DEFAULT_LOAD_FACTOR);
1.231 + }
1.232 +
1.233 + /**
1.234 + * Constructs a new, empty <tt>WeakHashMap</tt> with the default initial
1.235 + * capacity (16) and load factor (0.75).
1.236 + */
1.237 + public WeakHashMap() {
1.238 + this.loadFactor = DEFAULT_LOAD_FACTOR;
1.239 + threshold = DEFAULT_INITIAL_CAPACITY;
1.240 + table = newTable(DEFAULT_INITIAL_CAPACITY);
1.241 + }
1.242 +
1.243 + /**
1.244 + * Constructs a new <tt>WeakHashMap</tt> with the same mappings as the
1.245 + * specified map. The <tt>WeakHashMap</tt> is created with the default
1.246 + * load factor (0.75) and an initial capacity sufficient to hold the
1.247 + * mappings in the specified map.
1.248 + *
1.249 + * @param m the map whose mappings are to be placed in this map
1.250 + * @throws NullPointerException if the specified map is null
1.251 + * @since 1.3
1.252 + */
1.253 + public WeakHashMap(Map<? extends K, ? extends V> m) {
1.254 + this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, 16),
1.255 + DEFAULT_LOAD_FACTOR);
1.256 + putAll(m);
1.257 + }
1.258 +
1.259 + // internal utilities
1.260 +
1.261 + /**
1.262 + * Value representing null keys inside tables.
1.263 + */
1.264 + private static final Object NULL_KEY = new Object();
1.265 +
1.266 + /**
1.267 + * Use NULL_KEY for key if it is null.
1.268 + */
1.269 + private static Object maskNull(Object key) {
1.270 + return (key == null) ? NULL_KEY : key;
1.271 + }
1.272 +
1.273 + /**
1.274 + * Returns internal representation of null key back to caller as null.
1.275 + */
1.276 + static Object unmaskNull(Object key) {
1.277 + return (key == NULL_KEY) ? null : key;
1.278 + }
1.279 +
1.280 + /**
1.281 + * Checks for equality of non-null reference x and possibly-null y. By
1.282 + * default uses Object.equals.
1.283 + */
1.284 + private static boolean eq(Object x, Object y) {
1.285 + return x == y || x.equals(y);
1.286 + }
1.287 +
1.288 + /**
1.289 + * Returns index for hash code h.
1.290 + */
1.291 + private static int indexFor(int h, int length) {
1.292 + return h & (length-1);
1.293 + }
1.294 +
1.295 + /**
1.296 + * Expunges stale entries from the table.
1.297 + */
1.298 + private void expungeStaleEntries() {
1.299 + for (Object x; (x = queue.poll()) != null; ) {
1.300 + synchronized (queue) {
1.301 + @SuppressWarnings("unchecked")
1.302 + Entry<K,V> e = (Entry<K,V>) x;
1.303 + int i = indexFor(e.hash, table.length);
1.304 +
1.305 + Entry<K,V> prev = table[i];
1.306 + Entry<K,V> p = prev;
1.307 + while (p != null) {
1.308 + Entry<K,V> next = p.next;
1.309 + if (p == e) {
1.310 + if (prev == e)
1.311 + table[i] = next;
1.312 + else
1.313 + prev.next = next;
1.314 + // Must not null out e.next;
1.315 + // stale entries may be in use by a HashIterator
1.316 + e.value = null; // Help GC
1.317 + size--;
1.318 + break;
1.319 + }
1.320 + prev = p;
1.321 + p = next;
1.322 + }
1.323 + }
1.324 + }
1.325 + }
1.326 +
1.327 + /**
1.328 + * Returns the table after first expunging stale entries.
1.329 + */
1.330 + private Entry<K,V>[] getTable() {
1.331 + expungeStaleEntries();
1.332 + return table;
1.333 + }
1.334 +
1.335 + /**
1.336 + * Returns the number of key-value mappings in this map.
1.337 + * This result is a snapshot, and may not reflect unprocessed
1.338 + * entries that will be removed before next attempted access
1.339 + * because they are no longer referenced.
1.340 + */
1.341 + public int size() {
1.342 + if (size == 0)
1.343 + return 0;
1.344 + expungeStaleEntries();
1.345 + return size;
1.346 + }
1.347 +
1.348 + /**
1.349 + * Returns <tt>true</tt> if this map contains no key-value mappings.
1.350 + * This result is a snapshot, and may not reflect unprocessed
1.351 + * entries that will be removed before next attempted access
1.352 + * because they are no longer referenced.
1.353 + */
1.354 + public boolean isEmpty() {
1.355 + return size() == 0;
1.356 + }
1.357 +
1.358 + /**
1.359 + * Returns the value to which the specified key is mapped,
1.360 + * or {@code null} if this map contains no mapping for the key.
1.361 + *
1.362 + * <p>More formally, if this map contains a mapping from a key
1.363 + * {@code k} to a value {@code v} such that {@code (key==null ? k==null :
1.364 + * key.equals(k))}, then this method returns {@code v}; otherwise
1.365 + * it returns {@code null}. (There can be at most one such mapping.)
1.366 + *
1.367 + * <p>A return value of {@code null} does not <i>necessarily</i>
1.368 + * indicate that the map contains no mapping for the key; it's also
1.369 + * possible that the map explicitly maps the key to {@code null}.
1.370 + * The {@link #containsKey containsKey} operation may be used to
1.371 + * distinguish these two cases.
1.372 + *
1.373 + * @see #put(Object, Object)
1.374 + */
1.375 + public V get(Object key) {
1.376 + Object k = maskNull(key);
1.377 + int h = HashMap.hash(k.hashCode());
1.378 + Entry<K,V>[] tab = getTable();
1.379 + int index = indexFor(h, tab.length);
1.380 + Entry<K,V> e = tab[index];
1.381 + while (e != null) {
1.382 + if (e.hash == h && eq(k, e.get()))
1.383 + return e.value;
1.384 + e = e.next;
1.385 + }
1.386 + return null;
1.387 + }
1.388 +
1.389 + /**
1.390 + * Returns <tt>true</tt> if this map contains a mapping for the
1.391 + * specified key.
1.392 + *
1.393 + * @param key The key whose presence in this map is to be tested
1.394 + * @return <tt>true</tt> if there is a mapping for <tt>key</tt>;
1.395 + * <tt>false</tt> otherwise
1.396 + */
1.397 + public boolean containsKey(Object key) {
1.398 + return getEntry(key) != null;
1.399 + }
1.400 +
1.401 + /**
1.402 + * Returns the entry associated with the specified key in this map.
1.403 + * Returns null if the map contains no mapping for this key.
1.404 + */
1.405 + Entry<K,V> getEntry(Object key) {
1.406 + Object k = maskNull(key);
1.407 + int h = HashMap.hash(k.hashCode());
1.408 + Entry<K,V>[] tab = getTable();
1.409 + int index = indexFor(h, tab.length);
1.410 + Entry<K,V> e = tab[index];
1.411 + while (e != null && !(e.hash == h && eq(k, e.get())))
1.412 + e = e.next;
1.413 + return e;
1.414 + }
1.415 +
1.416 + /**
1.417 + * Associates the specified value with the specified key in this map.
1.418 + * If the map previously contained a mapping for this key, the old
1.419 + * value is replaced.
1.420 + *
1.421 + * @param key key with which the specified value is to be associated.
1.422 + * @param value value to be associated with the specified key.
1.423 + * @return the previous value associated with <tt>key</tt>, or
1.424 + * <tt>null</tt> if there was no mapping for <tt>key</tt>.
1.425 + * (A <tt>null</tt> return can also indicate that the map
1.426 + * previously associated <tt>null</tt> with <tt>key</tt>.)
1.427 + */
1.428 + public V put(K key, V value) {
1.429 + Object k = maskNull(key);
1.430 + int h = HashMap.hash(k.hashCode());
1.431 + Entry<K,V>[] tab = getTable();
1.432 + int i = indexFor(h, tab.length);
1.433 +
1.434 + for (Entry<K,V> e = tab[i]; e != null; e = e.next) {
1.435 + if (h == e.hash && eq(k, e.get())) {
1.436 + V oldValue = e.value;
1.437 + if (value != oldValue)
1.438 + e.value = value;
1.439 + return oldValue;
1.440 + }
1.441 + }
1.442 +
1.443 + modCount++;
1.444 + Entry<K,V> e = tab[i];
1.445 + tab[i] = new Entry<>(k, value, queue, h, e);
1.446 + if (++size >= threshold)
1.447 + resize(tab.length * 2);
1.448 + return null;
1.449 + }
1.450 +
1.451 + /**
1.452 + * Rehashes the contents of this map into a new array with a
1.453 + * larger capacity. This method is called automatically when the
1.454 + * number of keys in this map reaches its threshold.
1.455 + *
1.456 + * If current capacity is MAXIMUM_CAPACITY, this method does not
1.457 + * resize the map, but sets threshold to Integer.MAX_VALUE.
1.458 + * This has the effect of preventing future calls.
1.459 + *
1.460 + * @param newCapacity the new capacity, MUST be a power of two;
1.461 + * must be greater than current capacity unless current
1.462 + * capacity is MAXIMUM_CAPACITY (in which case value
1.463 + * is irrelevant).
1.464 + */
1.465 + void resize(int newCapacity) {
1.466 + Entry<K,V>[] oldTable = getTable();
1.467 + int oldCapacity = oldTable.length;
1.468 + if (oldCapacity == MAXIMUM_CAPACITY) {
1.469 + threshold = Integer.MAX_VALUE;
1.470 + return;
1.471 + }
1.472 +
1.473 + Entry<K,V>[] newTable = newTable(newCapacity);
1.474 + transfer(oldTable, newTable);
1.475 + table = newTable;
1.476 +
1.477 + /*
1.478 + * If ignoring null elements and processing ref queue caused massive
1.479 + * shrinkage, then restore old table. This should be rare, but avoids
1.480 + * unbounded expansion of garbage-filled tables.
1.481 + */
1.482 + if (size >= threshold / 2) {
1.483 + threshold = (int)(newCapacity * loadFactor);
1.484 + } else {
1.485 + expungeStaleEntries();
1.486 + transfer(newTable, oldTable);
1.487 + table = oldTable;
1.488 + }
1.489 + }
1.490 +
1.491 + /** Transfers all entries from src to dest tables */
1.492 + private void transfer(Entry<K,V>[] src, Entry<K,V>[] dest) {
1.493 + for (int j = 0; j < src.length; ++j) {
1.494 + Entry<K,V> e = src[j];
1.495 + src[j] = null;
1.496 + while (e != null) {
1.497 + Entry<K,V> next = e.next;
1.498 + Object key = e.get();
1.499 + if (key == null) {
1.500 + e.next = null; // Help GC
1.501 + e.value = null; // " "
1.502 + size--;
1.503 + } else {
1.504 + int i = indexFor(e.hash, dest.length);
1.505 + e.next = dest[i];
1.506 + dest[i] = e;
1.507 + }
1.508 + e = next;
1.509 + }
1.510 + }
1.511 + }
1.512 +
1.513 + /**
1.514 + * Copies all of the mappings from the specified map to this map.
1.515 + * These mappings will replace any mappings that this map had for any
1.516 + * of the keys currently in the specified map.
1.517 + *
1.518 + * @param m mappings to be stored in this map.
1.519 + * @throws NullPointerException if the specified map is null.
1.520 + */
1.521 + public void putAll(Map<? extends K, ? extends V> m) {
1.522 + int numKeysToBeAdded = m.size();
1.523 + if (numKeysToBeAdded == 0)
1.524 + return;
1.525 +
1.526 + /*
1.527 + * Expand the map if the map if the number of mappings to be added
1.528 + * is greater than or equal to threshold. This is conservative; the
1.529 + * obvious condition is (m.size() + size) >= threshold, but this
1.530 + * condition could result in a map with twice the appropriate capacity,
1.531 + * if the keys to be added overlap with the keys already in this map.
1.532 + * By using the conservative calculation, we subject ourself
1.533 + * to at most one extra resize.
1.534 + */
1.535 + if (numKeysToBeAdded > threshold) {
1.536 + int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);
1.537 + if (targetCapacity > MAXIMUM_CAPACITY)
1.538 + targetCapacity = MAXIMUM_CAPACITY;
1.539 + int newCapacity = table.length;
1.540 + while (newCapacity < targetCapacity)
1.541 + newCapacity <<= 1;
1.542 + if (newCapacity > table.length)
1.543 + resize(newCapacity);
1.544 + }
1.545 +
1.546 + for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
1.547 + put(e.getKey(), e.getValue());
1.548 + }
1.549 +
1.550 + /**
1.551 + * Removes the mapping for a key from this weak hash map if it is present.
1.552 + * More formally, if this map contains a mapping from key <tt>k</tt> to
1.553 + * value <tt>v</tt> such that <code>(key==null ? k==null :
1.554 + * key.equals(k))</code>, that mapping is removed. (The map can contain
1.555 + * at most one such mapping.)
1.556 + *
1.557 + * <p>Returns the value to which this map previously associated the key,
1.558 + * or <tt>null</tt> if the map contained no mapping for the key. A
1.559 + * return value of <tt>null</tt> does not <i>necessarily</i> indicate
1.560 + * that the map contained no mapping for the key; it's also possible
1.561 + * that the map explicitly mapped the key to <tt>null</tt>.
1.562 + *
1.563 + * <p>The map will not contain a mapping for the specified key once the
1.564 + * call returns.
1.565 + *
1.566 + * @param key key whose mapping is to be removed from the map
1.567 + * @return the previous value associated with <tt>key</tt>, or
1.568 + * <tt>null</tt> if there was no mapping for <tt>key</tt>
1.569 + */
1.570 + public V remove(Object key) {
1.571 + Object k = maskNull(key);
1.572 + int h = HashMap.hash(k.hashCode());
1.573 + Entry<K,V>[] tab = getTable();
1.574 + int i = indexFor(h, tab.length);
1.575 + Entry<K,V> prev = tab[i];
1.576 + Entry<K,V> e = prev;
1.577 +
1.578 + while (e != null) {
1.579 + Entry<K,V> next = e.next;
1.580 + if (h == e.hash && eq(k, e.get())) {
1.581 + modCount++;
1.582 + size--;
1.583 + if (prev == e)
1.584 + tab[i] = next;
1.585 + else
1.586 + prev.next = next;
1.587 + return e.value;
1.588 + }
1.589 + prev = e;
1.590 + e = next;
1.591 + }
1.592 +
1.593 + return null;
1.594 + }
1.595 +
1.596 + /** Special version of remove needed by Entry set */
1.597 + boolean removeMapping(Object o) {
1.598 + if (!(o instanceof Map.Entry))
1.599 + return false;
1.600 + Entry<K,V>[] tab = getTable();
1.601 + Map.Entry<?,?> entry = (Map.Entry<?,?>)o;
1.602 + Object k = maskNull(entry.getKey());
1.603 + int h = HashMap.hash(k.hashCode());
1.604 + int i = indexFor(h, tab.length);
1.605 + Entry<K,V> prev = tab[i];
1.606 + Entry<K,V> e = prev;
1.607 +
1.608 + while (e != null) {
1.609 + Entry<K,V> next = e.next;
1.610 + if (h == e.hash && e.equals(entry)) {
1.611 + modCount++;
1.612 + size--;
1.613 + if (prev == e)
1.614 + tab[i] = next;
1.615 + else
1.616 + prev.next = next;
1.617 + return true;
1.618 + }
1.619 + prev = e;
1.620 + e = next;
1.621 + }
1.622 +
1.623 + return false;
1.624 + }
1.625 +
1.626 + /**
1.627 + * Removes all of the mappings from this map.
1.628 + * The map will be empty after this call returns.
1.629 + */
1.630 + public void clear() {
1.631 + // clear out ref queue. We don't need to expunge entries
1.632 + // since table is getting cleared.
1.633 + while (queue.poll() != null)
1.634 + ;
1.635 +
1.636 + modCount++;
1.637 + Arrays.fill(table, null);
1.638 + size = 0;
1.639 +
1.640 + // Allocation of array may have caused GC, which may have caused
1.641 + // additional entries to go stale. Removing these entries from the
1.642 + // reference queue will make them eligible for reclamation.
1.643 + while (queue.poll() != null)
1.644 + ;
1.645 + }
1.646 +
1.647 + /**
1.648 + * Returns <tt>true</tt> if this map maps one or more keys to the
1.649 + * specified value.
1.650 + *
1.651 + * @param value value whose presence in this map is to be tested
1.652 + * @return <tt>true</tt> if this map maps one or more keys to the
1.653 + * specified value
1.654 + */
1.655 + public boolean containsValue(Object value) {
1.656 + if (value==null)
1.657 + return containsNullValue();
1.658 +
1.659 + Entry<K,V>[] tab = getTable();
1.660 + for (int i = tab.length; i-- > 0;)
1.661 + for (Entry<K,V> e = tab[i]; e != null; e = e.next)
1.662 + if (value.equals(e.value))
1.663 + return true;
1.664 + return false;
1.665 + }
1.666 +
1.667 + /**
1.668 + * Special-case code for containsValue with null argument
1.669 + */
1.670 + private boolean containsNullValue() {
1.671 + Entry<K,V>[] tab = getTable();
1.672 + for (int i = tab.length; i-- > 0;)
1.673 + for (Entry<K,V> e = tab[i]; e != null; e = e.next)
1.674 + if (e.value==null)
1.675 + return true;
1.676 + return false;
1.677 + }
1.678 +
1.679 + /**
1.680 + * The entries in this hash table extend WeakReference, using its main ref
1.681 + * field as the key.
1.682 + */
1.683 + private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
1.684 + V value;
1.685 + final int hash;
1.686 + Entry<K,V> next;
1.687 +
1.688 + /**
1.689 + * Creates new entry.
1.690 + */
1.691 + Entry(Object key, V value,
1.692 + ReferenceQueue<Object> queue,
1.693 + int hash, Entry<K,V> next) {
1.694 + super(key, queue);
1.695 + this.value = value;
1.696 + this.hash = hash;
1.697 + this.next = next;
1.698 + }
1.699 +
1.700 + @SuppressWarnings("unchecked")
1.701 + public K getKey() {
1.702 + return (K) WeakHashMap.unmaskNull(get());
1.703 + }
1.704 +
1.705 + public V getValue() {
1.706 + return value;
1.707 + }
1.708 +
1.709 + public V setValue(V newValue) {
1.710 + V oldValue = value;
1.711 + value = newValue;
1.712 + return oldValue;
1.713 + }
1.714 +
1.715 + public boolean equals(Object o) {
1.716 + if (!(o instanceof Map.Entry))
1.717 + return false;
1.718 + Map.Entry<?,?> e = (Map.Entry<?,?>)o;
1.719 + K k1 = getKey();
1.720 + Object k2 = e.getKey();
1.721 + if (k1 == k2 || (k1 != null && k1.equals(k2))) {
1.722 + V v1 = getValue();
1.723 + Object v2 = e.getValue();
1.724 + if (v1 == v2 || (v1 != null && v1.equals(v2)))
1.725 + return true;
1.726 + }
1.727 + return false;
1.728 + }
1.729 +
1.730 + public int hashCode() {
1.731 + K k = getKey();
1.732 + V v = getValue();
1.733 + return ((k==null ? 0 : k.hashCode()) ^
1.734 + (v==null ? 0 : v.hashCode()));
1.735 + }
1.736 +
1.737 + public String toString() {
1.738 + return getKey() + "=" + getValue();
1.739 + }
1.740 + }
1.741 +
1.742 + private abstract class HashIterator<T> implements Iterator<T> {
1.743 + private int index;
1.744 + private Entry<K,V> entry = null;
1.745 + private Entry<K,V> lastReturned = null;
1.746 + private int expectedModCount = modCount;
1.747 +
1.748 + /**
1.749 + * Strong reference needed to avoid disappearance of key
1.750 + * between hasNext and next
1.751 + */
1.752 + private Object nextKey = null;
1.753 +
1.754 + /**
1.755 + * Strong reference needed to avoid disappearance of key
1.756 + * between nextEntry() and any use of the entry
1.757 + */
1.758 + private Object currentKey = null;
1.759 +
1.760 + HashIterator() {
1.761 + index = isEmpty() ? 0 : table.length;
1.762 + }
1.763 +
1.764 + public boolean hasNext() {
1.765 + Entry<K,V>[] t = table;
1.766 +
1.767 + while (nextKey == null) {
1.768 + Entry<K,V> e = entry;
1.769 + int i = index;
1.770 + while (e == null && i > 0)
1.771 + e = t[--i];
1.772 + entry = e;
1.773 + index = i;
1.774 + if (e == null) {
1.775 + currentKey = null;
1.776 + return false;
1.777 + }
1.778 + nextKey = e.get(); // hold on to key in strong ref
1.779 + if (nextKey == null)
1.780 + entry = entry.next;
1.781 + }
1.782 + return true;
1.783 + }
1.784 +
1.785 + /** The common parts of next() across different types of iterators */
1.786 + protected Entry<K,V> nextEntry() {
1.787 + if (modCount != expectedModCount)
1.788 + throw new ConcurrentModificationException();
1.789 + if (nextKey == null && !hasNext())
1.790 + throw new NoSuchElementException();
1.791 +
1.792 + lastReturned = entry;
1.793 + entry = entry.next;
1.794 + currentKey = nextKey;
1.795 + nextKey = null;
1.796 + return lastReturned;
1.797 + }
1.798 +
1.799 + public void remove() {
1.800 + if (lastReturned == null)
1.801 + throw new IllegalStateException();
1.802 + if (modCount != expectedModCount)
1.803 + throw new ConcurrentModificationException();
1.804 +
1.805 + WeakHashMap.this.remove(currentKey);
1.806 + expectedModCount = modCount;
1.807 + lastReturned = null;
1.808 + currentKey = null;
1.809 + }
1.810 +
1.811 + }
1.812 +
1.813 + private class ValueIterator extends HashIterator<V> {
1.814 + public V next() {
1.815 + return nextEntry().value;
1.816 + }
1.817 + }
1.818 +
1.819 + private class KeyIterator extends HashIterator<K> {
1.820 + public K next() {
1.821 + return nextEntry().getKey();
1.822 + }
1.823 + }
1.824 +
1.825 + private class EntryIterator extends HashIterator<Map.Entry<K,V>> {
1.826 + public Map.Entry<K,V> next() {
1.827 + return nextEntry();
1.828 + }
1.829 + }
1.830 +
1.831 + // Views
1.832 +
1.833 + private transient Set<Map.Entry<K,V>> entrySet = null;
1.834 +
1.835 + /**
1.836 + * Returns a {@link Set} view of the keys contained in this map.
1.837 + * The set is backed by the map, so changes to the map are
1.838 + * reflected in the set, and vice-versa. If the map is modified
1.839 + * while an iteration over the set is in progress (except through
1.840 + * the iterator's own <tt>remove</tt> operation), the results of
1.841 + * the iteration are undefined. The set supports element removal,
1.842 + * which removes the corresponding mapping from the map, via the
1.843 + * <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
1.844 + * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
1.845 + * operations. It does not support the <tt>add</tt> or <tt>addAll</tt>
1.846 + * operations.
1.847 + */
1.848 + public Set<K> keySet() {
1.849 + Set<K> ks = keySet;
1.850 + return (ks != null ? ks : (keySet = new KeySet()));
1.851 + }
1.852 +
1.853 + private class KeySet extends AbstractSet<K> {
1.854 + public Iterator<K> iterator() {
1.855 + return new KeyIterator();
1.856 + }
1.857 +
1.858 + public int size() {
1.859 + return WeakHashMap.this.size();
1.860 + }
1.861 +
1.862 + public boolean contains(Object o) {
1.863 + return containsKey(o);
1.864 + }
1.865 +
1.866 + public boolean remove(Object o) {
1.867 + if (containsKey(o)) {
1.868 + WeakHashMap.this.remove(o);
1.869 + return true;
1.870 + }
1.871 + else
1.872 + return false;
1.873 + }
1.874 +
1.875 + public void clear() {
1.876 + WeakHashMap.this.clear();
1.877 + }
1.878 + }
1.879 +
1.880 + /**
1.881 + * Returns a {@link Collection} view of the values contained in this map.
1.882 + * The collection is backed by the map, so changes to the map are
1.883 + * reflected in the collection, and vice-versa. If the map is
1.884 + * modified while an iteration over the collection is in progress
1.885 + * (except through the iterator's own <tt>remove</tt> operation),
1.886 + * the results of the iteration are undefined. The collection
1.887 + * supports element removal, which removes the corresponding
1.888 + * mapping from the map, via the <tt>Iterator.remove</tt>,
1.889 + * <tt>Collection.remove</tt>, <tt>removeAll</tt>,
1.890 + * <tt>retainAll</tt> and <tt>clear</tt> operations. It does not
1.891 + * support the <tt>add</tt> or <tt>addAll</tt> operations.
1.892 + */
1.893 + public Collection<V> values() {
1.894 + Collection<V> vs = values;
1.895 + return (vs != null) ? vs : (values = new Values());
1.896 + }
1.897 +
1.898 + private class Values extends AbstractCollection<V> {
1.899 + public Iterator<V> iterator() {
1.900 + return new ValueIterator();
1.901 + }
1.902 +
1.903 + public int size() {
1.904 + return WeakHashMap.this.size();
1.905 + }
1.906 +
1.907 + public boolean contains(Object o) {
1.908 + return containsValue(o);
1.909 + }
1.910 +
1.911 + public void clear() {
1.912 + WeakHashMap.this.clear();
1.913 + }
1.914 + }
1.915 +
1.916 + /**
1.917 + * Returns a {@link Set} view of the mappings contained in this map.
1.918 + * The set is backed by the map, so changes to the map are
1.919 + * reflected in the set, and vice-versa. If the map is modified
1.920 + * while an iteration over the set is in progress (except through
1.921 + * the iterator's own <tt>remove</tt> operation, or through the
1.922 + * <tt>setValue</tt> operation on a map entry returned by the
1.923 + * iterator) the results of the iteration are undefined. The set
1.924 + * supports element removal, which removes the corresponding
1.925 + * mapping from the map, via the <tt>Iterator.remove</tt>,
1.926 + * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt> and
1.927 + * <tt>clear</tt> operations. It does not support the
1.928 + * <tt>add</tt> or <tt>addAll</tt> operations.
1.929 + */
1.930 + public Set<Map.Entry<K,V>> entrySet() {
1.931 + Set<Map.Entry<K,V>> es = entrySet;
1.932 + return es != null ? es : (entrySet = new EntrySet());
1.933 + }
1.934 +
1.935 + private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
1.936 + public Iterator<Map.Entry<K,V>> iterator() {
1.937 + return new EntryIterator();
1.938 + }
1.939 +
1.940 + public boolean contains(Object o) {
1.941 + if (!(o instanceof Map.Entry))
1.942 + return false;
1.943 + Map.Entry<?,?> e = (Map.Entry<?,?>)o;
1.944 + Entry<K,V> candidate = getEntry(e.getKey());
1.945 + return candidate != null && candidate.equals(e);
1.946 + }
1.947 +
1.948 + public boolean remove(Object o) {
1.949 + return removeMapping(o);
1.950 + }
1.951 +
1.952 + public int size() {
1.953 + return WeakHashMap.this.size();
1.954 + }
1.955 +
1.956 + public void clear() {
1.957 + WeakHashMap.this.clear();
1.958 + }
1.959 +
1.960 + private List<Map.Entry<K,V>> deepCopy() {
1.961 + List<Map.Entry<K,V>> list = new ArrayList<>(size());
1.962 + for (Map.Entry<K,V> e : this)
1.963 + list.add(new AbstractMap.SimpleEntry<>(e));
1.964 + return list;
1.965 + }
1.966 +
1.967 + public Object[] toArray() {
1.968 + return deepCopy().toArray();
1.969 + }
1.970 +
1.971 + public <T> T[] toArray(T[] a) {
1.972 + return deepCopy().toArray(a);
1.973 + }
1.974 + }
1.975 +}