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