| 1 | /* |
| 2 | * Copyright (C) Christian Schulte, 2005-206 |
| 3 | * All rights reserved. |
| 4 | * |
| 5 | * Redistribution and use in source and binary forms, with or without |
| 6 | * modification, are permitted provided that the following conditions |
| 7 | * are met: |
| 8 | * |
| 9 | * o Redistributions of source code must retain the above copyright |
| 10 | * notice, this list of conditions and the following disclaimer. |
| 11 | * |
| 12 | * o Redistributions in binary form must reproduce the above copyright |
| 13 | * notice, this list of conditions and the following disclaimer in |
| 14 | * the documentation and/or other materials provided with the |
| 15 | * distribution. |
| 16 | * |
| 17 | * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
| 18 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY |
| 19 | * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL |
| 20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, |
| 21 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| 22 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 24 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| 26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 27 | * |
| 28 | * $JOMC: WeakIdentityHashMap.java 4613 2012-09-22 10:07:08Z schulte $ |
| 29 | * |
| 30 | */ |
| 31 | package org.jomc.util; |
| 32 | |
| 33 | import java.lang.ref.ReferenceQueue; |
| 34 | import java.lang.ref.WeakReference; |
| 35 | import java.util.AbstractCollection; |
| 36 | import java.util.AbstractSet; |
| 37 | import java.util.Collection; |
| 38 | import java.util.ConcurrentModificationException; |
| 39 | import java.util.Iterator; |
| 40 | import java.util.Map; |
| 41 | import java.util.NoSuchElementException; |
| 42 | import java.util.Set; |
| 43 | |
| 44 | /** |
| 45 | * Hash-table based {@code Map} implementation with weak keys, using object-identity in place of object-equality when |
| 46 | * comparing keys. |
| 47 | * |
| 48 | * <p>In a {@code WeakIdentityHashMap} two keys {@code k1} and {@code k2} are considered equal if and only if |
| 49 | * {@code k1==k2} evaluates to {@code true}. An entry will automatically be removed when its key is no longer in |
| 50 | * ordinary use. More precisely, the presence of a mapping for a given key will not prevent the key from being discarded |
| 51 | * by the garbage collector, that is, made finalizable, finalised, and then reclaimed. When a key has been discarded its |
| 52 | * entry is effectively removed from the map, so this class behaves somewhat differently from other {@code Map} |
| 53 | * implementations and is not a general-purpose {@code Map} implementation! Although it implements the {@code Map} |
| 54 | * interface, it intentionally violates {@code Map}'s general contract, which mandates the use of the {@code equals} |
| 55 | * method when comparing objects.</p> |
| 56 | * |
| 57 | * <p>This class has performance characteristics similar to those of the {@code HashMap} class, and has the same |
| 58 | * efficiency parameters of {@code initialCapacity} and {@code loadFactor}. All of the optional map operations are |
| 59 | * provided. It does not support {@code null} values and the {@code null} key. All methods taking a key or value will |
| 60 | * throw a {@code NullPointerException} when given a {@code null} reference. No guarantees are made as to the order of |
| 61 | * the map; in particular, there is no guarantee that the order will remain constant over time. Like most collection |
| 62 | * classes, this class is not synchronised. A synchronised {@code WeakIdentityHashMap} may be constructed using the |
| 63 | * {@code Collections.synchronizedMap} method.</p> |
| 64 | * |
| 65 | * <p>The behaviour of the {@code WeakIdentityHashMap} class depends in part upon the actions of the garbage collector, |
| 66 | * so several {@code Map} invariants do not hold for this class. Because the garbage collector may discard keys at |
| 67 | * any time, a {@code WeakIdentityHashMap} may behave as though an unknown thread is silently removing entries. In |
| 68 | * particular, even if synchronising on a {@code WeakIdentityHashMap} instance and invoking none of its mutator |
| 69 | * methods, it is possible for the {@code size} method to return smaller values over time, for the {@code isEmpty} |
| 70 | * method to return {@code false} and then {@code true}, for the {@code containsKey} method to return {@code true} and |
| 71 | * later {@code false} for a given key, for the {@code get} method to return a value for a given key but later return |
| 72 | * {@code null}, for the {@code put} method to return {@code null} and the {@code remove} method to return {@code false} |
| 73 | * for a key that previously appeared to be in the map, and for successive examinations of the key set, the value |
| 74 | * collection, and the entry set to yield successively smaller numbers of elements. To protect against such situations |
| 75 | * all {@code Iterator}s and {@code Entry}s created by this class throw an {@code IllegalStateException} when a key |
| 76 | * becomes {@code null} or a mapping got removed.</p> |
| 77 | * |
| 78 | * <p>The iterators returned by the {@code iterator} method of the collections returned by all of this classes |
| 79 | * "collection view methods" are fail-fast: if the map is structurally modified at any time after the iterator is |
| 80 | * created, in any way except through the iterator's own {@code remove} method, the iterator will throw a |
| 81 | * {@code ConcurrentModificationException}. Note that the fail-fast behaviour of an iterator cannot be guaranteed as it |
| 82 | * is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronised concurrent |
| 83 | * modification. Fail-fast iterators throw {@code ConcurrentModificationException}s on a best-effort basis. Therefore, |
| 84 | * it would be wrong to write a program that depends on this exception for its correctness: <i>the fail-fast behaviour |
| 85 | * of iterators should be used only to detect bugs.</i></p> |
| 86 | * |
| 87 | * @param <K> The type of keys maintained by this map. |
| 88 | * @param <V> The type of mapped values. |
| 89 | * |
| 90 | * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> |
| 91 | * @version $JOMC: WeakIdentityHashMap.java 4613 2012-09-22 10:07:08Z schulte $ |
| 92 | * |
| 93 | * @see java.util.HashMap |
| 94 | * @see java.util.WeakHashMap |
| 95 | * @see java.util.IdentityHashMap |
| 96 | * @see java.util.Collections#synchronizedMap |
| 97 | */ |
| 98 | public final class WeakIdentityHashMap<K, V> implements Map<K, V> |
| 99 | { |
| 100 | |
| 101 | /** Maximum capacity of the hash-table backing the implementation ({@code 2^30}). */ |
| 102 | private static final int MAXIMUM_CAPACITY = 0x40000000; |
| 103 | |
| 104 | /** Default initial capacity ({@code 2^4}). */ |
| 105 | private static final int DEFAULT_INITIAL_CAPACITY = 16; |
| 106 | |
| 107 | /** Default load factor ({@code 0.75}). */ |
| 108 | private static final float DEFAULT_LOAD_FACTOR = 0.75F; |
| 109 | |
| 110 | /** The number of times the map got structurally modified. */ |
| 111 | private int modifications; |
| 112 | |
| 113 | /** The number of mappings held by the map. */ |
| 114 | private int size; |
| 115 | |
| 116 | /** The size value at which the hash table needs resizing. */ |
| 117 | private int resizeThreshold; |
| 118 | |
| 119 | /** The hash-table backing the map. */ |
| 120 | private WeakEntry<K, V>[] hashTable; |
| 121 | |
| 122 | /** Queue, to which weak keys are appended to. */ |
| 123 | private final ReferenceQueue<K> referenceQueue = new ReferenceQueue<K>(); |
| 124 | |
| 125 | /** The key set view of the map. */ |
| 126 | private transient Set<K> keySet; |
| 127 | |
| 128 | /** The entry set view of the map. */ |
| 129 | private transient Set<Map.Entry<K, V>> entrySet; |
| 130 | |
| 131 | /** The value collection view of the map. */ |
| 132 | private transient Collection<V> valueCollection; |
| 133 | |
| 134 | /** The initial capacity of the hash table. */ |
| 135 | private final int initialCapacity; |
| 136 | |
| 137 | /** The load factor for the hash table. */ |
| 138 | private final float loadFactor; |
| 139 | |
| 140 | /** Null value returned by method {@link #getEntry(Object)}. */ |
| 141 | private final WeakEntry<K, V> NULL_ENTRY = new WeakEntry<K, V>( null, null, 0, this.referenceQueue ); |
| 142 | |
| 143 | /** |
| 144 | * Constructs a new, empty {@code WeakIdentityHashMap} with the default initial capacity ({@code 16}) and load |
| 145 | * factor ({@code 0.75}). |
| 146 | */ |
| 147 | public WeakIdentityHashMap() |
| 148 | { |
| 149 | this( DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR ); |
| 150 | } |
| 151 | |
| 152 | /** |
| 153 | * Constructs a new, empty {@code WeakIdentityHashMap} with the given initial capacity and the default load factor |
| 154 | * ({@code 0.75}). |
| 155 | * |
| 156 | * @param initialCapacity The initial capacity of the {@code WeakIdentityHashMap}. |
| 157 | * |
| 158 | * @throws IllegalArgumentException if {@code initialCapacity} is negative or greater than the maximum supported |
| 159 | * capacity ({@code 2^30}). |
| 160 | */ |
| 161 | public WeakIdentityHashMap( final int initialCapacity ) |
| 162 | { |
| 163 | this( initialCapacity, DEFAULT_LOAD_FACTOR ); |
| 164 | } |
| 165 | |
| 166 | /** |
| 167 | * Constructs a new, empty {@code WeakIdentityHashMap} with the default initial capacity ({@code 16}) and the given |
| 168 | * load factor. |
| 169 | * |
| 170 | * @param loadFactor The load factor of the {@code WeakIdentityHashMap}. |
| 171 | * |
| 172 | * @throws IllegalArgumentException if {@code loadFactor} is nonpositive. |
| 173 | */ |
| 174 | public WeakIdentityHashMap( final float loadFactor ) |
| 175 | { |
| 176 | this( DEFAULT_INITIAL_CAPACITY, loadFactor ); |
| 177 | } |
| 178 | |
| 179 | /** |
| 180 | * Constructs a new, empty {@code WeakIdentityHashMap} with the given initial capacity and the given load factor. |
| 181 | * |
| 182 | * @param initialCapacity The initial capacity of the {@code WeakIdentityHashMap}. |
| 183 | * @param loadFactor The load factor of the {@code WeakIdentityHashMap}. |
| 184 | * |
| 185 | * @throws IllegalArgumentException if {@code initialCapacity} is negative or greater than the maximum supported |
| 186 | * capacity ({@code 2^30}), or if {@code loadFactor} is nonpositive. |
| 187 | */ |
| 188 | public WeakIdentityHashMap( final int initialCapacity, final float loadFactor ) |
| 189 | { |
| 190 | if ( initialCapacity < 0 || initialCapacity > MAXIMUM_CAPACITY ) |
| 191 | { |
| 192 | throw new IllegalArgumentException( Integer.toString( initialCapacity ) ); |
| 193 | } |
| 194 | if ( loadFactor <= 0 || Float.isNaN( loadFactor ) ) |
| 195 | { |
| 196 | throw new IllegalArgumentException( Float.toString( loadFactor ) ); |
| 197 | } |
| 198 | |
| 199 | this.initialCapacity = initialCapacity; |
| 200 | this.loadFactor = loadFactor; |
| 201 | this.resizeThreshold = initialCapacity; |
| 202 | this.size = 0; |
| 203 | this.hashTable = new WeakEntry[ initialCapacity ]; |
| 204 | } |
| 205 | |
| 206 | /** |
| 207 | * Gets the number of key-value mappings in this map. |
| 208 | * <p>If the map contains more than {@code Integer.MAX_VALUE} elements, returns {@code Integer.MAX_VALUE}.</p> |
| 209 | * |
| 210 | * @return The number of key-value mappings in this map. |
| 211 | */ |
| 212 | public int size() |
| 213 | { |
| 214 | if ( this.size > 0 ) |
| 215 | { |
| 216 | this.purge(); |
| 217 | } |
| 218 | |
| 219 | return this.size; |
| 220 | } |
| 221 | |
| 222 | /** |
| 223 | * Gets a flag indicating if this map is empty. |
| 224 | * |
| 225 | * @return {@code true}, if this map contains no key-value mappings; {@code false}, if this map contains at least |
| 226 | * one mapping. |
| 227 | */ |
| 228 | public boolean isEmpty() |
| 229 | { |
| 230 | return this.size() == 0; |
| 231 | } |
| 232 | |
| 233 | /** |
| 234 | * Gets a flag indicating if this map contains a mapping for a given key. |
| 235 | * <p>More formally, returns {@code true}, if and only if this map contains a mapping for a key {@code k} such that |
| 236 | * {@code key==k}. There can be at most one such mapping.</p> |
| 237 | * |
| 238 | * @param key The key whose presence in this map is to be tested. |
| 239 | * |
| 240 | * @return {@code true}, if this map contains a mapping for {@code key}; {@code false}, if this map does not contain |
| 241 | * a mapping for {@code key}. |
| 242 | * |
| 243 | * @throws NullPointerException if {@code key} is {@code null}. |
| 244 | */ |
| 245 | public boolean containsKey( final Object key ) |
| 246 | { |
| 247 | if ( key == null ) |
| 248 | { |
| 249 | throw new NullPointerException( "key" ); |
| 250 | } |
| 251 | |
| 252 | return this.getEntry( key ).value != null; |
| 253 | } |
| 254 | |
| 255 | /** |
| 256 | * Gets a flag indicating if this map maps one or more keys to the specified value. |
| 257 | * <p>More formally, this method returns {@code true}, if and only if this map contains at least one mapping to a |
| 258 | * value {@code v} such that {@code value.equals(v)}. This operation requires time linear in the map size.</p> |
| 259 | * |
| 260 | * @param value The value whose presence in this map is to be tested. |
| 261 | * |
| 262 | * @return {@code true}, if this map maps one or more keys to {@code value}; {@code false}, if this map does not map |
| 263 | * any key to {@code value}. |
| 264 | * |
| 265 | * @throws NullPointerException if {@code value} is {@code null}. |
| 266 | */ |
| 267 | public boolean containsValue( final Object value ) |
| 268 | { |
| 269 | if ( value == null ) |
| 270 | { |
| 271 | throw new NullPointerException( "value" ); |
| 272 | } |
| 273 | |
| 274 | final WeakEntry<K, V>[] table = this.getHashTable(); |
| 275 | |
| 276 | for ( int i = table.length - 1; i >= 0; i-- ) |
| 277 | { |
| 278 | for ( WeakEntry<K, V> e = table[i]; e != null; e = e.next ) |
| 279 | { |
| 280 | if ( value.equals( e.value ) ) |
| 281 | { |
| 282 | return true; |
| 283 | } |
| 284 | } |
| 285 | } |
| 286 | |
| 287 | return false; |
| 288 | } |
| 289 | |
| 290 | /** |
| 291 | * Gets the value to which a given key is mapped or {@code null}, if this map contains no mapping for that key. |
| 292 | * <p>More formally, if this map contains a mapping from a key {@code k} to a value {@code v} such that |
| 293 | * {@code key==k}, then this method returns {@code v}; otherwise it returns {@code null}. There can be at most one |
| 294 | * such mapping.</p> |
| 295 | * |
| 296 | * @param key The key whose associated value is to be returned. |
| 297 | * |
| 298 | * @return The value to which {@code key} is mapped or {@code null}, if this map contains no mapping for |
| 299 | * {@code key}. |
| 300 | * |
| 301 | * @throws NullPointerException if {@code key} is {@code null}. |
| 302 | */ |
| 303 | public V get( final Object key ) |
| 304 | { |
| 305 | if ( key == null ) |
| 306 | { |
| 307 | throw new NullPointerException( "key" ); |
| 308 | } |
| 309 | |
| 310 | return this.getEntry( key ).value; |
| 311 | } |
| 312 | |
| 313 | /** |
| 314 | * Associates a given value with a given key in this map. |
| 315 | * <p>If the map previously contained a mapping for that key, the old value is replaced by the given value.</p> |
| 316 | * |
| 317 | * @param key The key with which {@code value} is to be associated. |
| 318 | * @param value The value to be associated with {@code key}. |
| 319 | * |
| 320 | * @return The value previously associated with {@code key} or {@code null}, if there was no mapping for |
| 321 | * {@code key}. |
| 322 | * |
| 323 | * @throws NullPointerException if {@code key} or {@code value} is {@code null}. |
| 324 | */ |
| 325 | public V put( final K key, final V value ) |
| 326 | { |
| 327 | if ( key == null ) |
| 328 | { |
| 329 | throw new NullPointerException( "key" ); |
| 330 | } |
| 331 | if ( value == null ) |
| 332 | { |
| 333 | throw new NullPointerException( "value" ); |
| 334 | } |
| 335 | |
| 336 | final int hashCode = System.identityHashCode( key ); |
| 337 | final WeakEntry<K, V>[] table = this.getHashTable(); |
| 338 | final int index = getHashTableIndex( hashCode, table.length ); |
| 339 | |
| 340 | for ( WeakEntry<K, V> e = table[index]; e != null; e = e.next ) |
| 341 | { |
| 342 | if ( e.hashCode == hashCode && e.get() == key ) |
| 343 | { |
| 344 | final V oldValue = e.value; |
| 345 | e.value = value; |
| 346 | return oldValue; |
| 347 | } |
| 348 | } |
| 349 | |
| 350 | final WeakEntry<K, V> entry = new WeakEntry<K, V>( key, value, hashCode, this.referenceQueue ); |
| 351 | entry.next = table[index]; |
| 352 | table[index] = entry; |
| 353 | |
| 354 | this.increaseSize(); |
| 355 | |
| 356 | return null; |
| 357 | } |
| 358 | |
| 359 | /** |
| 360 | * Removes the mapping for a given key from this map if it is present. |
| 361 | * <p>More formally, if this map contains a mapping from key {@code k} to value {@code v} such that {@code key==k}, |
| 362 | * that mapping is removed. The map can contain at most one such mapping. The map will not contain a mapping for the |
| 363 | * given key once the call returns.</p> |
| 364 | * |
| 365 | * @param key The key whose mapping is to be removed from the map. |
| 366 | * |
| 367 | * @return The value previously associated with {@code key} or {@code null}, if there was no mapping for |
| 368 | * {@code key}. |
| 369 | * |
| 370 | * @throws NullPointerException if {@code key} is {@code null}. |
| 371 | */ |
| 372 | public V remove( final Object key ) |
| 373 | { |
| 374 | if ( key == null ) |
| 375 | { |
| 376 | throw new NullPointerException( "key" ); |
| 377 | } |
| 378 | |
| 379 | final WeakEntry<K, V>[] table = this.getHashTable(); |
| 380 | final int hashCode = System.identityHashCode( key ); |
| 381 | final int index = getHashTableIndex( hashCode, table.length ); |
| 382 | |
| 383 | for ( WeakEntry<K, V> e = table[index], pre = null; e != null; pre = e, e = e.next ) |
| 384 | { |
| 385 | if ( e.hashCode == hashCode && e.get() == key ) |
| 386 | { |
| 387 | if ( pre != null ) |
| 388 | { |
| 389 | pre.next = e.next; |
| 390 | } |
| 391 | else |
| 392 | { |
| 393 | table[index] = e.next; |
| 394 | } |
| 395 | |
| 396 | this.decreaseSize(); |
| 397 | |
| 398 | final V removed = e.value; |
| 399 | e.removed = true; |
| 400 | e.value = null; |
| 401 | e.next = null; |
| 402 | return removed; |
| 403 | } |
| 404 | } |
| 405 | |
| 406 | return null; |
| 407 | } |
| 408 | |
| 409 | /** |
| 410 | * Copies all of the mappings from a given map to this map. |
| 411 | * <p>The effect of this call is equivalent to that of calling {@link #put(Object,Object) put(k, v)} on this map |
| 412 | * once for each mapping from key {@code k} to value {@code v} in the given map. The behavior of this operation is |
| 413 | * undefined if the given map is modified while the operation is in progress.</p> |
| 414 | * |
| 415 | * @param m The mappings to be stored in this map. |
| 416 | * |
| 417 | * @throws NullPointerException if {@code map} is {@code null}, or if {@code map} contains {@code null} keys or |
| 418 | * values. |
| 419 | */ |
| 420 | public void putAll( final Map<? extends K, ? extends V> m ) |
| 421 | { |
| 422 | if ( m == null ) |
| 423 | { |
| 424 | throw new NullPointerException( "m" ); |
| 425 | } |
| 426 | |
| 427 | for ( final Iterator<? extends Map.Entry<? extends K, ? extends V>> it = m.entrySet().iterator(); |
| 428 | it.hasNext(); ) |
| 429 | { |
| 430 | final Map.Entry<? extends K, ? extends V> entry = it.next(); |
| 431 | this.put( entry.getKey(), entry.getValue() ); |
| 432 | } |
| 433 | } |
| 434 | |
| 435 | /** Removes all of the mappings from this map so that the map will be empty after this call returns. */ |
| 436 | @SuppressWarnings( "empty-statement" ) |
| 437 | public void clear() |
| 438 | { |
| 439 | this.purge(); |
| 440 | this.hashTable = new WeakEntry[ this.initialCapacity ]; |
| 441 | this.size = 0; |
| 442 | this.resizeThreshold = this.initialCapacity; |
| 443 | this.modifications++; |
| 444 | while ( this.referenceQueue.poll() != null ); |
| 445 | } |
| 446 | |
| 447 | /** |
| 448 | * Gets a {@code Set} view of the keys contained in this map. |
| 449 | * <p>The set is backed by the map, so changes to the map are reflected in the set, and vice-versa. If the map is |
| 450 | * modified while an iteration over the set is in progress (except through the iterator's own {@code remove} |
| 451 | * operation), the results of the iteration are undefined, that is, the iterator may throw an |
| 452 | * {@code IllegalStateException}. The set supports element removal, which removes the corresponding mapping from the |
| 453 | * map, via the {@code Iterator.remove}, {@code Set.remove}, {@code removeAll}, {@code retainAll}, and {@code clear} |
| 454 | * operations. It does not support the {@code add} or {@code addAll} operations.</p> |
| 455 | * |
| 456 | * @return A set view of the keys contained in this map. |
| 457 | */ |
| 458 | public Set<K> keySet() |
| 459 | { |
| 460 | if ( this.keySet == null ) |
| 461 | { |
| 462 | this.keySet = new AbstractSet<K>() |
| 463 | { |
| 464 | |
| 465 | public Iterator<K> iterator() |
| 466 | { |
| 467 | return new KeyIterator(); |
| 468 | } |
| 469 | |
| 470 | public int size() |
| 471 | { |
| 472 | return WeakIdentityHashMap.this.size(); |
| 473 | } |
| 474 | |
| 475 | }; |
| 476 | |
| 477 | } |
| 478 | |
| 479 | return this.keySet; |
| 480 | } |
| 481 | |
| 482 | /** |
| 483 | * Gets a {@code Collection} view of the values contained in this map. |
| 484 | * <p>The collection is backed by the map, so changes to the map are reflected in the collection, and vice-versa. |
| 485 | * If the map is modified while an iteration over the collection is in progress (except through the iterator's own |
| 486 | * {@code remove} operation), the results of the iteration are undefined, that is, the iterator may throw an |
| 487 | * {@code IllegalStateException}. The collection supports element removal, which removes the corresponding mapping |
| 488 | * from the map, via the {@code Iterator.remove}, {@code Collection.remove}, {@code removeAll}, {@code retainAll} |
| 489 | * and {@code clear} operations. It does not support the {@code add} or {@code addAll} operations.</p> |
| 490 | * |
| 491 | * @return A collection view of the values contained in this map. |
| 492 | */ |
| 493 | public Collection<V> values() |
| 494 | { |
| 495 | if ( this.valueCollection == null ) |
| 496 | { |
| 497 | this.valueCollection = new AbstractCollection<V>() |
| 498 | { |
| 499 | |
| 500 | public Iterator<V> iterator() |
| 501 | { |
| 502 | return new ValueIterator(); |
| 503 | } |
| 504 | |
| 505 | public int size() |
| 506 | { |
| 507 | return WeakIdentityHashMap.this.size(); |
| 508 | } |
| 509 | |
| 510 | }; |
| 511 | } |
| 512 | |
| 513 | return this.valueCollection; |
| 514 | } |
| 515 | |
| 516 | /** |
| 517 | * Gets a {@code Set} view of the mappings contained in this map. |
| 518 | * <p>The set is backed by the map, so changes to the map are reflected in the set, and vice-versa. If the map is |
| 519 | * modified while an iteration over the set is in progress (except through the iterator's own {@code remove} |
| 520 | * operation, or through the {@code setValue} operation on a map entry returned by the iterator) the results of the |
| 521 | * iteration are undefined, that is, the iterator may throw an {@code IllegalStateException}. The set supports |
| 522 | * element removal, which removes the corresponding mapping from the map, via the {@code Iterator.remove}, |
| 523 | * {@code Set.remove}, {@code removeAll}, {@code retainAll} and {@code clear} operations. It does not support the |
| 524 | * {@code add} or {@code addAll} operations.</p> |
| 525 | * |
| 526 | * @return A set view of the mappings contained in this map. |
| 527 | */ |
| 528 | public Set<Map.Entry<K, V>> entrySet() |
| 529 | { |
| 530 | if ( this.entrySet == null ) |
| 531 | { |
| 532 | this.entrySet = new AbstractSet<Map.Entry<K, V>>() |
| 533 | { |
| 534 | |
| 535 | public Iterator<Map.Entry<K, V>> iterator() |
| 536 | { |
| 537 | return new EntryIterator(); |
| 538 | } |
| 539 | |
| 540 | public int size() |
| 541 | { |
| 542 | return WeakIdentityHashMap.this.size(); |
| 543 | } |
| 544 | |
| 545 | }; |
| 546 | } |
| 547 | |
| 548 | return this.entrySet; |
| 549 | } |
| 550 | |
| 551 | /** |
| 552 | * Returns a string representation of the object. |
| 553 | * |
| 554 | * @return A string representation of the object. |
| 555 | */ |
| 556 | @Override |
| 557 | public String toString() |
| 558 | { |
| 559 | return super.toString() + this.internalString(); |
| 560 | } |
| 561 | |
| 562 | /** |
| 563 | * Compares the specified object with this map for equality. |
| 564 | * <p>Returns {@code true}, if the given object is also a map and the two maps represent the same mappings. More |
| 565 | * formally, two maps {@code m1} and {@code m2} represent the same mappings if |
| 566 | * {@code m1.entrySet().equals(m2.entrySet())}.</p> |
| 567 | * |
| 568 | * @param o The object to be compared for equality with this map. |
| 569 | * |
| 570 | * @return {@code true}, if {@code o} is equal to this map; {@code false}, if {@code o} is not equal to this map. |
| 571 | */ |
| 572 | @Override |
| 573 | public boolean equals( final Object o ) |
| 574 | { |
| 575 | boolean equal = this == o; |
| 576 | |
| 577 | if ( !equal && o instanceof Map<?, ?> ) |
| 578 | { |
| 579 | final Map<?, ?> that = (Map<?, ?>) o; |
| 580 | equal = this.entrySet().equals( that.entrySet() ); |
| 581 | } |
| 582 | |
| 583 | return equal; |
| 584 | } |
| 585 | |
| 586 | /** |
| 587 | * Gets the hash code value for this map. |
| 588 | * <p>The hash code of a map is defined to be the sum of the hash codes of each entry in the map's |
| 589 | * {@code entrySet()} view.</p> |
| 590 | * |
| 591 | * @return The hash code value for this map. |
| 592 | */ |
| 593 | @Override |
| 594 | public int hashCode() |
| 595 | { |
| 596 | return this.entrySet().hashCode(); |
| 597 | } |
| 598 | |
| 599 | /** |
| 600 | * Finalizes the object by polling the internal reference queue for any pending references. |
| 601 | * |
| 602 | * @since 1.2 |
| 603 | */ |
| 604 | @Override |
| 605 | protected void finalize() throws Throwable |
| 606 | { |
| 607 | this.modifications++; |
| 608 | while ( this.referenceQueue.poll() != null ); |
| 609 | super.finalize(); |
| 610 | } |
| 611 | |
| 612 | /** |
| 613 | * Creates a string representing the mappings of the instance. |
| 614 | * |
| 615 | * @return A string representing the mappings of the instance. |
| 616 | */ |
| 617 | private String internalString() |
| 618 | { |
| 619 | final StringBuilder buf = new StringBuilder( 12 * this.size() ).append( '{' ); |
| 620 | final WeakEntry<K, V>[] table = this.getHashTable(); |
| 621 | for ( int i = table.length - 1, index = 0; i >= 0; i-- ) |
| 622 | { |
| 623 | for ( WeakEntry<K, V> e = table[i]; e != null; e = e.next ) |
| 624 | { |
| 625 | if ( buf.length() > 1 ) |
| 626 | { |
| 627 | buf.append( ", " ); |
| 628 | } |
| 629 | |
| 630 | buf.append( '[' ).append( index++ ).append( "]=" ).append( e ); |
| 631 | } |
| 632 | } |
| 633 | |
| 634 | return buf.append( '}' ).toString(); |
| 635 | } |
| 636 | |
| 637 | /** |
| 638 | * Gets the index of a hash code value. |
| 639 | * |
| 640 | * @param hashCode The hash code value to return the index of. |
| 641 | * @param capacity The capacity to return an index for. |
| 642 | * |
| 643 | * @return The index of {@code hashCode} for {@code capacity}. |
| 644 | */ |
| 645 | private static int getHashTableIndex( final int hashCode, final int capacity ) |
| 646 | { |
| 647 | return hashCode & ( capacity - 1 ); |
| 648 | } |
| 649 | |
| 650 | /** |
| 651 | * Gets the hash-table backing the instance. |
| 652 | * <p>This method creates a new hash-table and re-hashes any mappings whenever the size of the map gets greater than |
| 653 | * the capacity of the internal hash-table times the load factor value.</p> |
| 654 | * |
| 655 | * @return The hash-table backing the instance. |
| 656 | */ |
| 657 | private WeakEntry<K, V>[] getHashTable() |
| 658 | { |
| 659 | if ( this.hashTable.length < this.resizeThreshold ) |
| 660 | { |
| 661 | @SuppressWarnings( "unchecked" ) |
| 662 | final WeakEntry<K, V>[] table = new WeakEntry[ this.calculateCapacity() ]; |
| 663 | |
| 664 | for ( int i = this.hashTable.length - 1; i >= 0; i-- ) |
| 665 | { |
| 666 | WeakEntry<K, V> entry = this.hashTable[i]; |
| 667 | |
| 668 | while ( entry != null ) |
| 669 | { |
| 670 | final WeakEntry<K, V> next = entry.next; |
| 671 | final int index = getHashTableIndex( entry.hashCode, table.length ); |
| 672 | |
| 673 | entry.next = table[index]; |
| 674 | table[index] = entry; |
| 675 | entry = next; |
| 676 | } |
| 677 | } |
| 678 | |
| 679 | this.hashTable = table; |
| 680 | this.modifications++; |
| 681 | } |
| 682 | |
| 683 | this.purge(); |
| 684 | return this.hashTable; |
| 685 | } |
| 686 | |
| 687 | /** Removes any garbage collected entries. */ |
| 688 | private void purge() |
| 689 | { |
| 690 | WeakEntry<K, V> purge; |
| 691 | |
| 692 | while ( ( purge = (WeakEntry<K, V>) this.referenceQueue.poll() ) != null ) |
| 693 | { |
| 694 | final int index = getHashTableIndex( purge.hashCode, this.hashTable.length ); |
| 695 | |
| 696 | for ( WeakEntry<K, V> e = this.hashTable[index], pre = null; e != null; pre = e, e = e.next ) |
| 697 | { |
| 698 | if ( e == purge ) |
| 699 | { |
| 700 | if ( pre != null ) |
| 701 | { |
| 702 | pre.next = purge.next; |
| 703 | } |
| 704 | else |
| 705 | { |
| 706 | this.hashTable[index] = purge.next; |
| 707 | } |
| 708 | |
| 709 | purge.removed = true; |
| 710 | purge.next = null; |
| 711 | purge.value = null; |
| 712 | |
| 713 | this.decreaseSize(); |
| 714 | |
| 715 | break; |
| 716 | } |
| 717 | } |
| 718 | } |
| 719 | } |
| 720 | |
| 721 | private void increaseSize() |
| 722 | { |
| 723 | if ( this.size < Integer.MAX_VALUE ) |
| 724 | { |
| 725 | this.size++; |
| 726 | this.resizeThreshold = (int) ( this.size * this.loadFactor ); |
| 727 | } |
| 728 | |
| 729 | this.modifications++; |
| 730 | } |
| 731 | |
| 732 | private void decreaseSize() |
| 733 | { |
| 734 | if ( this.size > 0 ) |
| 735 | { |
| 736 | this.size--; |
| 737 | } |
| 738 | |
| 739 | this.modifications++; |
| 740 | } |
| 741 | |
| 742 | private int calculateCapacity() |
| 743 | { |
| 744 | int maxCapacity = this.initialCapacity; |
| 745 | if ( maxCapacity < this.resizeThreshold ) |
| 746 | { |
| 747 | maxCapacity = this.resizeThreshold > MAXIMUM_CAPACITY ? MAXIMUM_CAPACITY : this.resizeThreshold; |
| 748 | } |
| 749 | |
| 750 | int capacity = 1; |
| 751 | while ( capacity < maxCapacity ) |
| 752 | { |
| 753 | capacity <<= 1; |
| 754 | } |
| 755 | |
| 756 | return capacity; |
| 757 | } |
| 758 | |
| 759 | private WeakEntry<K, V> getEntry( final Object key ) |
| 760 | { |
| 761 | final int hashCode = System.identityHashCode( key ); |
| 762 | final WeakEntry<K, V>[] table = getHashTable(); |
| 763 | |
| 764 | for ( WeakEntry<K, V> e = table[getHashTableIndex( hashCode, table.length )]; e != null; e = e.next ) |
| 765 | { |
| 766 | if ( e.hashCode == hashCode && e.get() == key ) |
| 767 | { |
| 768 | return e; |
| 769 | } |
| 770 | } |
| 771 | |
| 772 | return NULL_ENTRY; |
| 773 | } |
| 774 | |
| 775 | /** |
| 776 | * A map entry (key-value pair) with weakly referenced key. |
| 777 | * <p>The {@code WeakIdentityHashMap.entrySet} method returns a collection-view of the map, whose elements are of |
| 778 | * this class. The only way to obtain a reference to a map entry is from the iterator of this collection-view. These |
| 779 | * {@code Map.Entry} objects are valid only for the duration of the iteration; more formally, the behavior of a map |
| 780 | * entry is undefined if the backing map has been modified after the entry was returned by the iterator, except |
| 781 | * through the {@code setValue} operation on the map entry.</p> |
| 782 | * |
| 783 | * @param <K> The type of the key. |
| 784 | * @param <V> The type of the value. |
| 785 | * |
| 786 | * @see WeakIdentityHashMap#entrySet() |
| 787 | */ |
| 788 | private static class WeakEntry<K, V> extends WeakReference<K> implements Map.Entry<K, V> |
| 789 | { |
| 790 | |
| 791 | /** The value of the entry. */ |
| 792 | private V value; |
| 793 | |
| 794 | /** The next entry in the bucket. */ |
| 795 | private WeakEntry<K, V> next; |
| 796 | |
| 797 | /** Flag indicating that this entry got removed from the map. */ |
| 798 | private boolean removed; |
| 799 | |
| 800 | /** The hash code value of the key. */ |
| 801 | private final int hashCode; |
| 802 | |
| 803 | WeakEntry( final K key, final V value, final int hashCode, final ReferenceQueue<K> queue ) |
| 804 | { |
| 805 | super( key, queue ); |
| 806 | this.hashCode = hashCode; |
| 807 | this.value = value; |
| 808 | } |
| 809 | |
| 810 | /** |
| 811 | * Gets the key corresponding to this entry. |
| 812 | * |
| 813 | * @return The key corresponding to this entry. |
| 814 | * |
| 815 | * @throws IllegalStateException if the entry got removed from the backing map (either due to an iterator's |
| 816 | * {@code remove} operation or due to the key having been garbage collected). |
| 817 | */ |
| 818 | public K getKey() |
| 819 | { |
| 820 | final K key = this.get(); |
| 821 | |
| 822 | if ( key == null || this.removed ) |
| 823 | { |
| 824 | throw new IllegalStateException(); |
| 825 | } |
| 826 | |
| 827 | return key; |
| 828 | } |
| 829 | |
| 830 | /** |
| 831 | * Gets the value corresponding to this entry. |
| 832 | * |
| 833 | * @return The value corresponding to this entry. |
| 834 | * |
| 835 | * @throws IllegalStateException if the entry got removed from the backing map (either due to an iterator's |
| 836 | * {@code remove} operation or due to the key having been garbage collected). |
| 837 | */ |
| 838 | public V getValue() |
| 839 | { |
| 840 | if ( this.get() == null || this.removed ) |
| 841 | { |
| 842 | throw new IllegalStateException(); |
| 843 | } |
| 844 | |
| 845 | return this.value; |
| 846 | } |
| 847 | |
| 848 | /** |
| 849 | * Replaces the value corresponding to this entry with the specified value. |
| 850 | * |
| 851 | * @param value The new value to be stored in this entry. |
| 852 | * |
| 853 | * @return The old value corresponding to the entry. |
| 854 | * |
| 855 | * @throws NullPointerException if {@code value} is {@code null}. |
| 856 | * @throws IllegalStateException if the entry got removed from the backing map (either due to an iterator's |
| 857 | * {@code remove} operation or due to the key having been garbage collected). |
| 858 | */ |
| 859 | public V setValue( final V value ) |
| 860 | { |
| 861 | if ( value == null ) |
| 862 | { |
| 863 | throw new NullPointerException( "value" ); |
| 864 | } |
| 865 | if ( this.get() == null || this.removed ) |
| 866 | { |
| 867 | throw new IllegalStateException(); |
| 868 | } |
| 869 | |
| 870 | final V oldValue = this.getValue(); |
| 871 | |
| 872 | if ( value != oldValue && !value.equals( oldValue ) ) |
| 873 | { |
| 874 | this.value = value; |
| 875 | } |
| 876 | |
| 877 | return oldValue; |
| 878 | } |
| 879 | |
| 880 | /** |
| 881 | * Returns a string representation of the object. |
| 882 | * |
| 883 | * @return A string representation of the object. |
| 884 | */ |
| 885 | @Override |
| 886 | public String toString() |
| 887 | { |
| 888 | return super.toString() + this.internalString(); |
| 889 | } |
| 890 | |
| 891 | /** |
| 892 | * Compares a given object with this entry for equality. |
| 893 | * <p>Returns {@code true}, if the given object is also a map entry and the two entries represent the same |
| 894 | * mapping. More formally, two entries {@code e1} and {@code e2} represent the same mapping if |
| 895 | * <pre><blockquote> |
| 896 | * ( e1.getKey() == e2.getKey() ) && |
| 897 | * ( e1.getValue().equals( e2.getValue() ) ) |
| 898 | * </blockquote></pre></p> |
| 899 | * |
| 900 | * @param o The object to be compared for equality with this map entry. |
| 901 | * |
| 902 | * @return {@code true}, if {@code o} is equal to this map entry; {@code false}, if {@code o} is not equal to |
| 903 | * this map entry. |
| 904 | */ |
| 905 | @Override |
| 906 | public boolean equals( final Object o ) |
| 907 | { |
| 908 | boolean equal = this == o; |
| 909 | |
| 910 | if ( !equal && o instanceof Map.Entry<?, ?> ) |
| 911 | { |
| 912 | final Map.Entry<?, ?> that = (Map.Entry<?, ?>) o; |
| 913 | equal = this.getKey() == that.getKey() && this.getValue().equals( that.getValue() ); |
| 914 | } |
| 915 | |
| 916 | return equal; |
| 917 | } |
| 918 | |
| 919 | /** |
| 920 | * Gets the hash code value for this map entry. |
| 921 | * <p>The hash code of a map entry {@code e} is defined to be: |
| 922 | * <pre><blockquote> |
| 923 | * ( e.getKey() == null ? 0 : e.getKey().hashCode() ) ^ |
| 924 | * ( e.getValue() == null ? 0 : e.getValue().hashCode() ) |
| 925 | * </blockquote></pre></p> |
| 926 | * |
| 927 | * @return The hash code value for this map entry. |
| 928 | */ |
| 929 | @Override |
| 930 | public int hashCode() |
| 931 | { |
| 932 | return ( this.hashCode ) ^ ( this.getValue().hashCode() ); |
| 933 | } |
| 934 | |
| 935 | /** |
| 936 | * Creates a string representing the properties of the instance. |
| 937 | * |
| 938 | * @return A string representing the properties of the instance. |
| 939 | */ |
| 940 | private String internalString() |
| 941 | { |
| 942 | final StringBuilder buf = new StringBuilder( 50 ).append( '{' ); |
| 943 | buf.append( "key=" ).append( this.getKey() ).append( ", value=" ).append( this.getValue() ); |
| 944 | return buf.append( '}' ).toString(); |
| 945 | } |
| 946 | |
| 947 | } |
| 948 | |
| 949 | /** Base iterator implementation over the hash-table backing the implementation. */ |
| 950 | private class WeakEntryIterator |
| 951 | { |
| 952 | |
| 953 | /** The next element in the iteration. */ |
| 954 | private WeakEntry<K, V> next; |
| 955 | |
| 956 | /** The current element in the iteration. */ |
| 957 | private WeakEntry<K, V> current; |
| 958 | |
| 959 | /** The current index into the hash-table. */ |
| 960 | private int index; |
| 961 | |
| 962 | /** The number of modifications when this iterator got created. */ |
| 963 | private int modifications; |
| 964 | |
| 965 | /** Creates a new {@code WeakEntryIterator} instance. */ |
| 966 | WeakEntryIterator() |
| 967 | { |
| 968 | final WeakEntry<K, V>[] table = getHashTable(); |
| 969 | for ( this.index = table.length - 1; this.index >= 0; this.index-- ) |
| 970 | { |
| 971 | if ( table[this.index] != null ) |
| 972 | { |
| 973 | this.next = table[this.index--]; |
| 974 | break; |
| 975 | } |
| 976 | } |
| 977 | |
| 978 | this.modifications = WeakIdentityHashMap.this.modifications; |
| 979 | } |
| 980 | |
| 981 | /** |
| 982 | * Gets a flag indicating that the iteration has more elements. |
| 983 | * |
| 984 | * @return {@code true}, if the iterator has more elements; {@code false}, if the iterator does not have more |
| 985 | * elements. |
| 986 | */ |
| 987 | public boolean hasNext() |
| 988 | { |
| 989 | if ( this.modifications != WeakIdentityHashMap.this.modifications ) |
| 990 | { |
| 991 | throw new ConcurrentModificationException(); |
| 992 | } |
| 993 | |
| 994 | return this.next != null; |
| 995 | } |
| 996 | |
| 997 | /** |
| 998 | * Gets the next element in the iteration. |
| 999 | * |
| 1000 | * @return The next element in the iteration. |
| 1001 | * |
| 1002 | * @throws NoSuchElementException if the iterator does not have more elements. |
| 1003 | */ |
| 1004 | public Map.Entry<K, V> nextElement() |
| 1005 | { |
| 1006 | if ( this.modifications != WeakIdentityHashMap.this.modifications ) |
| 1007 | { |
| 1008 | throw new ConcurrentModificationException(); |
| 1009 | } |
| 1010 | if ( this.next == null ) |
| 1011 | { |
| 1012 | throw new NoSuchElementException(); |
| 1013 | } |
| 1014 | |
| 1015 | this.current = this.next; |
| 1016 | |
| 1017 | if ( this.next.next != null ) |
| 1018 | { |
| 1019 | this.next = this.next.next; |
| 1020 | } |
| 1021 | else |
| 1022 | { |
| 1023 | this.next = null; |
| 1024 | final WeakEntry<K, V>[] table = getHashTable(); |
| 1025 | for ( ; this.index >= 0; this.index-- ) |
| 1026 | { |
| 1027 | if ( table[this.index] != null ) |
| 1028 | { |
| 1029 | this.next = table[this.index--]; |
| 1030 | break; |
| 1031 | } |
| 1032 | } |
| 1033 | } |
| 1034 | |
| 1035 | return this.current; |
| 1036 | } |
| 1037 | |
| 1038 | /** |
| 1039 | * Removes from the underlying hash-table the last element returned by the iterator. |
| 1040 | * |
| 1041 | * @throws IllegalStateException if the {@code next} method has not yet been called, or the {@code remove} |
| 1042 | * method has already been called after the last call to the {@code next} method. |
| 1043 | */ |
| 1044 | public void remove() |
| 1045 | { |
| 1046 | if ( this.modifications != WeakIdentityHashMap.this.modifications ) |
| 1047 | { |
| 1048 | throw new ConcurrentModificationException(); |
| 1049 | } |
| 1050 | if ( this.current == null ) |
| 1051 | { |
| 1052 | throw new IllegalStateException(); |
| 1053 | } |
| 1054 | |
| 1055 | final K key = this.current.getKey(); |
| 1056 | |
| 1057 | if ( key == null ) |
| 1058 | { |
| 1059 | throw new IllegalStateException(); |
| 1060 | } |
| 1061 | |
| 1062 | WeakIdentityHashMap.this.remove( key ); |
| 1063 | this.modifications = WeakIdentityHashMap.this.modifications; |
| 1064 | this.current = null; |
| 1065 | } |
| 1066 | |
| 1067 | } |
| 1068 | |
| 1069 | /** Iterator over the hash-table backing the implementation. */ |
| 1070 | private class EntryIterator extends WeakEntryIterator implements Iterator<Map.Entry<K, V>> |
| 1071 | { |
| 1072 | |
| 1073 | /** Creates a new {@code EntryIterator} instance. */ |
| 1074 | EntryIterator() |
| 1075 | { |
| 1076 | super(); |
| 1077 | } |
| 1078 | |
| 1079 | /** |
| 1080 | * Gets the next element in the iteration. |
| 1081 | * |
| 1082 | * @return The next element in the iteration. |
| 1083 | * |
| 1084 | * @throws NoSuchElementException if the iterator does not have more elements. |
| 1085 | */ |
| 1086 | public Map.Entry<K, V> next() |
| 1087 | { |
| 1088 | return super.nextElement(); |
| 1089 | } |
| 1090 | |
| 1091 | } |
| 1092 | |
| 1093 | /** Iterator over the hash-table backing the implementation. */ |
| 1094 | private class KeyIterator extends WeakEntryIterator implements Iterator<K> |
| 1095 | { |
| 1096 | |
| 1097 | /** Creates a new {@code KeyIterator} instance. */ |
| 1098 | KeyIterator() |
| 1099 | { |
| 1100 | super(); |
| 1101 | } |
| 1102 | |
| 1103 | /** |
| 1104 | * Gets the next element in the iteration. |
| 1105 | * |
| 1106 | * @return The next element in the iteration. |
| 1107 | * |
| 1108 | * @throws NoSuchElementException if the iterator does not have more elements. |
| 1109 | */ |
| 1110 | public K next() |
| 1111 | { |
| 1112 | return super.nextElement().getKey(); |
| 1113 | } |
| 1114 | |
| 1115 | } |
| 1116 | |
| 1117 | /** Iterator over the hash-table backing the implementation. */ |
| 1118 | private class ValueIterator extends WeakEntryIterator implements Iterator<V> |
| 1119 | { |
| 1120 | |
| 1121 | /** Creates a new {@code ValueIterator} instance. */ |
| 1122 | ValueIterator() |
| 1123 | { |
| 1124 | super(); |
| 1125 | } |
| 1126 | |
| 1127 | /** |
| 1128 | * Gets the next element in the iteration. |
| 1129 | * |
| 1130 | * @return The next element in the iteration. |
| 1131 | * |
| 1132 | * @throws NoSuchElementException if the iterator does not have more elements. |
| 1133 | */ |
| 1134 | public V next() |
| 1135 | { |
| 1136 | return super.nextElement().getValue(); |
| 1137 | } |
| 1138 | |
| 1139 | } |
| 1140 | |
| 1141 | } |