001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.collections4.map; 018 019import java.io.IOException; 020import java.io.ObjectInputStream; 021import java.io.ObjectOutputStream; 022import java.lang.ref.Reference; 023import java.lang.ref.ReferenceQueue; 024import java.lang.ref.SoftReference; 025import java.lang.ref.WeakReference; 026import java.util.ArrayList; 027import java.util.Collection; 028import java.util.ConcurrentModificationException; 029import java.util.Iterator; 030import java.util.List; 031import java.util.Map; 032import java.util.NoSuchElementException; 033import java.util.Set; 034 035import org.apache.commons.collections4.MapIterator; 036import org.apache.commons.collections4.keyvalue.DefaultMapEntry; 037 038/** 039 * An abstract implementation of a hash-based map that allows the entries to 040 * be removed by the garbage collector. 041 * <p> 042 * This class implements all the features necessary for a subclass reference 043 * hash-based map. Key-value entries are stored in instances of the 044 * <code>ReferenceEntry</code> class which can be overridden and replaced. 045 * The iterators can similarly be replaced, without the need to replace the KeySet, 046 * EntrySet and Values view classes. 047 * <p> 048 * Overridable methods are provided to change the default hashing behaviour, and 049 * to change how entries are added to and removed from the map. Hopefully, all you 050 * need for unusual subclasses is here. 051 * <p> 052 * When you construct an <code>AbstractReferenceMap</code>, you can specify what 053 * kind of references are used to store the map's keys and values. 054 * If non-hard references are used, then the garbage collector can remove 055 * mappings if a key or value becomes unreachable, or if the JVM's memory is 056 * running low. For information on how the different reference types behave, 057 * see {@link Reference}. 058 * <p> 059 * Different types of references can be specified for keys and values. 060 * The keys can be configured to be weak but the values hard, 061 * in which case this class will behave like a 062 * <a href="http://java.sun.com/j2se/1.4/docs/api/java/util/WeakHashMap.html"> 063 * <code>WeakHashMap</code></a>. However, you can also specify hard keys and 064 * weak values, or any other combination. The default constructor uses 065 * hard keys and soft values, providing a memory-sensitive cache. 066 * <p> 067 * This {@link Map} implementation does <i>not</i> allow null elements. 068 * Attempting to add a null key or value to the map will raise a 069 * <code>NullPointerException</code>. 070 * <p> 071 * All the available iterators can be reset back to the start by casting to 072 * <code>ResettableIterator</code> and calling <code>reset()</code>. 073 * <p> 074 * This implementation is not synchronized. 075 * You can use {@link java.util.Collections#synchronizedMap} to 076 * provide synchronized access to a <code>ReferenceMap</code>. 077 * 078 * @param <K> the type of the keys in this map 079 * @param <V> the type of the values in this map 080 * 081 * @see java.lang.ref.Reference 082 * @since 3.1 (extracted from ReferenceMap in 3.0) 083 */ 084public abstract class AbstractReferenceMap<K, V> extends AbstractHashedMap<K, V> { 085 086 /** 087 * Reference type enum. 088 */ 089 public enum ReferenceStrength { 090 HARD(0), SOFT(1), WEAK(2); 091 092 /** value */ 093 public final int value; 094 095 /** 096 * Resolve enum from int. 097 * @param value the int value 098 * @return ReferenceType 099 * @throws IllegalArgumentException if the specified value is invalid. 100 */ 101 public static ReferenceStrength resolve(final int value) { 102 switch (value) { 103 case 0: 104 return HARD; 105 case 1: 106 return SOFT; 107 case 2: 108 return WEAK; 109 default: 110 throw new IllegalArgumentException(); 111 } 112 } 113 114 private ReferenceStrength(final int value) { 115 this.value = value; 116 } 117 118 } 119 120 /** 121 * The reference type for keys. 122 */ 123 private ReferenceStrength keyType; 124 125 /** 126 * The reference type for values. 127 */ 128 private ReferenceStrength valueType; 129 130 /** 131 * Should the value be automatically purged when the associated key has been collected? 132 */ 133 private boolean purgeValues; 134 135 /** 136 * ReferenceQueue used to eliminate stale mappings. 137 * See purge. 138 */ 139 private transient ReferenceQueue<Object> queue; 140 141 //----------------------------------------------------------------------- 142 /** 143 * Constructor used during deserialization. 144 */ 145 protected AbstractReferenceMap() { 146 super(); 147 } 148 149 /** 150 * Constructs a new empty map with the specified reference types, 151 * load factor and initial capacity. 152 * 153 * @param keyType the type of reference to use for keys; 154 * must be {@link ReferenceStrength#HARD HARD}, 155 * {@link ReferenceStrength#SOFT SOFT}, 156 * {@link ReferenceStrength#WEAK WEAK} 157 * @param valueType the type of reference to use for values; 158 * must be {@link ReferenceStrength#HARD}, 159 * {@link ReferenceStrength#SOFT SOFT}, 160 * {@link ReferenceStrength#WEAK WEAK} 161 * @param capacity the initial capacity for the map 162 * @param loadFactor the load factor for the map 163 * @param purgeValues should the value be automatically purged when the 164 * key is garbage collected 165 */ 166 protected AbstractReferenceMap( 167 final ReferenceStrength keyType, final ReferenceStrength valueType, final int capacity, 168 final float loadFactor, final boolean purgeValues) { 169 super(capacity, loadFactor); 170 this.keyType = keyType; 171 this.valueType = valueType; 172 this.purgeValues = purgeValues; 173 } 174 175 /** 176 * Initialise this subclass during construction, cloning or deserialization. 177 */ 178 @Override 179 protected void init() { 180 queue = new ReferenceQueue<>(); 181 } 182 183 //----------------------------------------------------------------------- 184 /** 185 * Gets the size of the map. 186 * 187 * @return the size 188 */ 189 @Override 190 public int size() { 191 purgeBeforeRead(); 192 return super.size(); 193 } 194 195 /** 196 * Checks whether the map is currently empty. 197 * 198 * @return true if the map is currently size zero 199 */ 200 @Override 201 public boolean isEmpty() { 202 purgeBeforeRead(); 203 return super.isEmpty(); 204 } 205 206 /** 207 * Checks whether the map contains the specified key. 208 * 209 * @param key the key to search for 210 * @return true if the map contains the key 211 */ 212 @Override 213 public boolean containsKey(final Object key) { 214 purgeBeforeRead(); 215 final Entry<K, V> entry = getEntry(key); 216 if (entry == null) { 217 return false; 218 } 219 return entry.getValue() != null; 220 } 221 222 /** 223 * Checks whether the map contains the specified value. 224 * 225 * @param value the value to search for 226 * @return true if the map contains the value 227 */ 228 @Override 229 public boolean containsValue(final Object value) { 230 purgeBeforeRead(); 231 if (value == null) { 232 return false; 233 } 234 return super.containsValue(value); 235 } 236 237 /** 238 * Gets the value mapped to the key specified. 239 * 240 * @param key the key 241 * @return the mapped value, null if no match 242 */ 243 @Override 244 public V get(final Object key) { 245 purgeBeforeRead(); 246 final Entry<K, V> entry = getEntry(key); 247 if (entry == null) { 248 return null; 249 } 250 return entry.getValue(); 251 } 252 253 254 /** 255 * Puts a key-value mapping into this map. 256 * Neither the key nor the value may be null. 257 * 258 * @param key the key to add, must not be null 259 * @param value the value to add, must not be null 260 * @return the value previously mapped to this key, null if none 261 * @throws NullPointerException if either the key or value is null 262 */ 263 @Override 264 public V put(final K key, final V value) { 265 if (key == null) { 266 throw new NullPointerException("null keys not allowed"); 267 } 268 if (value == null) { 269 throw new NullPointerException("null values not allowed"); 270 } 271 272 purgeBeforeWrite(); 273 return super.put(key, value); 274 } 275 276 /** 277 * Removes the specified mapping from this map. 278 * 279 * @param key the mapping to remove 280 * @return the value mapped to the removed key, null if key not in map 281 */ 282 @Override 283 public V remove(final Object key) { 284 if (key == null) { 285 return null; 286 } 287 purgeBeforeWrite(); 288 return super.remove(key); 289 } 290 291 /** 292 * Clears this map. 293 */ 294 @Override 295 public void clear() { 296 super.clear(); 297 // drain the queue 298 while (queue.poll() != null) {} // NOPMD 299 } 300 301 //----------------------------------------------------------------------- 302 /** 303 * Gets a MapIterator over the reference map. 304 * The iterator only returns valid key/value pairs. 305 * 306 * @return a map iterator 307 */ 308 @Override 309 public MapIterator<K, V> mapIterator() { 310 return new ReferenceMapIterator<>(this); 311 } 312 313 /** 314 * Returns a set view of this map's entries. 315 * An iterator returned entry is valid until <code>next()</code> is called again. 316 * The <code>setValue()</code> method on the <code>toArray</code> entries has no effect. 317 * 318 * @return a set view of this map's entries 319 */ 320 @Override 321 public Set<Map.Entry<K, V>> entrySet() { 322 if (entrySet == null) { 323 entrySet = new ReferenceEntrySet<>(this); 324 } 325 return entrySet; 326 } 327 328 /** 329 * Returns a set view of this map's keys. 330 * 331 * @return a set view of this map's keys 332 */ 333 @Override 334 public Set<K> keySet() { 335 if (keySet == null) { 336 keySet = new ReferenceKeySet<>(this); 337 } 338 return keySet; 339 } 340 341 /** 342 * Returns a collection view of this map's values. 343 * 344 * @return a set view of this map's values 345 */ 346 @Override 347 public Collection<V> values() { 348 if (values == null) { 349 values = new ReferenceValues<>(this); 350 } 351 return values; 352 } 353 354 //----------------------------------------------------------------------- 355 /** 356 * Purges stale mappings from this map before read operations. 357 * <p> 358 * This implementation calls {@link #purge()} to maintain a consistent state. 359 */ 360 protected void purgeBeforeRead() { 361 purge(); 362 } 363 364 /** 365 * Purges stale mappings from this map before write operations. 366 * <p> 367 * This implementation calls {@link #purge()} to maintain a consistent state. 368 */ 369 protected void purgeBeforeWrite() { 370 purge(); 371 } 372 373 /** 374 * Purges stale mappings from this map. 375 * <p> 376 * Note that this method is not synchronized! Special 377 * care must be taken if, for instance, you want stale 378 * mappings to be removed on a periodic basis by some 379 * background thread. 380 */ 381 protected void purge() { 382 Reference<?> ref = queue.poll(); 383 while (ref != null) { 384 purge(ref); 385 ref = queue.poll(); 386 } 387 } 388 389 /** 390 * Purges the specified reference. 391 * 392 * @param ref the reference to purge 393 */ 394 protected void purge(final Reference<?> ref) { 395 // The hashCode of the reference is the hashCode of the 396 // mapping key, even if the reference refers to the 397 // mapping value... 398 final int hash = ref.hashCode(); 399 final int index = hashIndex(hash, data.length); 400 HashEntry<K, V> previous = null; 401 HashEntry<K, V> entry = data[index]; 402 while (entry != null) { 403 ReferenceEntry<K, V> refEntry = (ReferenceEntry<K, V>) entry; 404 if (refEntry.purge(ref)) { 405 if (previous == null) { 406 data[index] = entry.next; 407 } else { 408 previous.next = entry.next; 409 } 410 this.size--; 411 refEntry.onPurge(); 412 return; 413 } 414 previous = entry; 415 entry = entry.next; 416 } 417 418 } 419 420 //----------------------------------------------------------------------- 421 /** 422 * Gets the entry mapped to the key specified. 423 * 424 * @param key the key 425 * @return the entry, null if no match 426 */ 427 @Override 428 protected HashEntry<K, V> getEntry(final Object key) { 429 if (key == null) { 430 return null; 431 } 432 return super.getEntry(key); 433 } 434 435 /** 436 * Gets the hash code for a MapEntry. 437 * Subclasses can override this, for example to use the identityHashCode. 438 * 439 * @param key the key to get a hash code for, may be null 440 * @param value the value to get a hash code for, may be null 441 * @return the hash code, as per the MapEntry specification 442 */ 443 protected int hashEntry(final Object key, final Object value) { 444 return (key == null ? 0 : key.hashCode()) ^ 445 (value == null ? 0 : value.hashCode()); 446 } 447 448 /** 449 * Compares two keys, in internal converted form, to see if they are equal. 450 * <p> 451 * This implementation converts the key from the entry to a real reference 452 * before comparison. 453 * 454 * @param key1 the first key to compare passed in from outside 455 * @param key2 the second key extracted from the entry via <code>entry.key</code> 456 * @return true if equal 457 */ 458 @Override 459 @SuppressWarnings("unchecked") 460 protected boolean isEqualKey(final Object key1, Object key2) { 461 key2 = keyType == ReferenceStrength.HARD ? key2 : ((Reference<K>) key2).get(); 462 return key1 == key2 || key1.equals(key2); 463 } 464 465 /** 466 * Creates a ReferenceEntry instead of a HashEntry. 467 * 468 * @param next the next entry in sequence 469 * @param hashCode the hash code to use 470 * @param key the key to store 471 * @param value the value to store 472 * @return the newly created entry 473 */ 474 @Override 475 protected ReferenceEntry<K, V> createEntry(final HashEntry<K, V> next, final int hashCode, 476 final K key, final V value) { 477 return new ReferenceEntry<>(this, next, hashCode, key, value); 478 } 479 480 /** 481 * Creates an entry set iterator. 482 * 483 * @return the entrySet iterator 484 */ 485 @Override 486 protected Iterator<Map.Entry<K, V>> createEntrySetIterator() { 487 return new ReferenceEntrySetIterator<>(this); 488 } 489 490 /** 491 * Creates an key set iterator. 492 * 493 * @return the keySet iterator 494 */ 495 @Override 496 protected Iterator<K> createKeySetIterator() { 497 return new ReferenceKeySetIterator<>(this); 498 } 499 500 /** 501 * Creates an values iterator. 502 * 503 * @return the values iterator 504 */ 505 @Override 506 protected Iterator<V> createValuesIterator() { 507 return new ReferenceValuesIterator<>(this); 508 } 509 510 //----------------------------------------------------------------------- 511 /** 512 * EntrySet implementation. 513 */ 514 static class ReferenceEntrySet<K, V> extends EntrySet<K, V> { 515 516 protected ReferenceEntrySet(final AbstractHashedMap<K, V> parent) { 517 super(parent); 518 } 519 520 @Override 521 public Object[] toArray() { 522 return toArray(new Object[size()]); 523 } 524 525 @Override 526 public <T> T[] toArray(final T[] arr) { 527 // special implementation to handle disappearing entries 528 final ArrayList<Map.Entry<K, V>> list = new ArrayList<>(size()); 529 for (final Map.Entry<K, V> entry : this) { 530 list.add(new DefaultMapEntry<>(entry)); 531 } 532 return list.toArray(arr); 533 } 534 } 535 536 //----------------------------------------------------------------------- 537 /** 538 * KeySet implementation. 539 */ 540 static class ReferenceKeySet<K> extends KeySet<K> { 541 542 protected ReferenceKeySet(final AbstractHashedMap<K, ?> parent) { 543 super(parent); 544 } 545 546 @Override 547 public Object[] toArray() { 548 return toArray(new Object[size()]); 549 } 550 551 @Override 552 public <T> T[] toArray(final T[] arr) { 553 // special implementation to handle disappearing keys 554 final List<K> list = new ArrayList<>(size()); 555 for (final K key : this) { 556 list.add(key); 557 } 558 return list.toArray(arr); 559 } 560 } 561 562 //----------------------------------------------------------------------- 563 /** 564 * Values implementation. 565 */ 566 static class ReferenceValues<V> extends Values<V> { 567 568 protected ReferenceValues(final AbstractHashedMap<?, V> parent) { 569 super(parent); 570 } 571 572 @Override 573 public Object[] toArray() { 574 return toArray(new Object[size()]); 575 } 576 577 @Override 578 public <T> T[] toArray(final T[] arr) { 579 // special implementation to handle disappearing values 580 final List<V> list = new ArrayList<>(size()); 581 for (final V value : this) { 582 list.add(value); 583 } 584 return list.toArray(arr); 585 } 586 } 587 588 //----------------------------------------------------------------------- 589 /** 590 * A MapEntry implementation for the map. 591 * <p> 592 * If getKey() or getValue() returns null, it means 593 * the mapping is stale and should be removed. 594 * 595 * @since 3.1 596 */ 597 protected static class ReferenceEntry<K, V> extends HashEntry<K, V> { 598 /** The parent map */ 599 private final AbstractReferenceMap<K, V> parent; 600 601 /** 602 * Creates a new entry object for the ReferenceMap. 603 * 604 * @param parent the parent map 605 * @param next the next entry in the hash bucket 606 * @param hashCode the hash code of the key 607 * @param key the key 608 * @param value the value 609 */ 610 public ReferenceEntry(final AbstractReferenceMap<K, V> parent, final HashEntry<K, V> next, 611 final int hashCode, final K key, final V value) { 612 super(next, hashCode, null, null); 613 this.parent = parent; 614 this.key = toReference(parent.keyType, key, hashCode); 615 this.value = toReference(parent.valueType, value, hashCode); // the key hashCode is passed in deliberately 616 } 617 618 /** 619 * Gets the key from the entry. 620 * This method dereferences weak and soft keys and thus may return null. 621 * 622 * @return the key, which may be null if it was garbage collected 623 */ 624 @Override 625 @SuppressWarnings("unchecked") 626 public K getKey() { 627 return (K) (parent.keyType == ReferenceStrength.HARD ? key : ((Reference<K>) key).get()); 628 } 629 630 /** 631 * Gets the value from the entry. 632 * This method dereferences weak and soft value and thus may return null. 633 * 634 * @return the value, which may be null if it was garbage collected 635 */ 636 @Override 637 @SuppressWarnings("unchecked") 638 public V getValue() { 639 return (V) (parent.valueType == ReferenceStrength.HARD ? value : ((Reference<V>) value).get()); 640 } 641 642 /** 643 * Sets the value of the entry. 644 * 645 * @param obj the object to store 646 * @return the previous value 647 */ 648 @Override 649 @SuppressWarnings("unchecked") 650 public V setValue(final V obj) { 651 final V old = getValue(); 652 if (parent.valueType != ReferenceStrength.HARD) { 653 ((Reference<V>) value).clear(); 654 } 655 value = toReference(parent.valueType, obj, hashCode); 656 return old; 657 } 658 659 /** 660 * Compares this map entry to another. 661 * <p> 662 * This implementation uses <code>isEqualKey</code> and 663 * <code>isEqualValue</code> on the main map for comparison. 664 * 665 * @param obj the other map entry to compare to 666 * @return true if equal, false if not 667 */ 668 @Override 669 public boolean equals(final Object obj) { 670 if (obj == this) { 671 return true; 672 } 673 if (obj instanceof Map.Entry == false) { 674 return false; 675 } 676 677 final Map.Entry<?, ?> entry = (Map.Entry<?, ?>)obj; 678 final Object entryKey = entry.getKey(); // convert to hard reference 679 final Object entryValue = entry.getValue(); // convert to hard reference 680 if (entryKey == null || entryValue == null) { 681 return false; 682 } 683 // compare using map methods, aiding identity subclass 684 // note that key is direct access and value is via method 685 return parent.isEqualKey(entryKey, key) && 686 parent.isEqualValue(entryValue, getValue()); 687 } 688 689 /** 690 * Gets the hashcode of the entry using temporary hard references. 691 * <p> 692 * This implementation uses <code>hashEntry</code> on the main map. 693 * 694 * @return the hashcode of the entry 695 */ 696 @Override 697 public int hashCode() { 698 return parent.hashEntry(getKey(), getValue()); 699 } 700 701 /** 702 * Constructs a reference of the given type to the given referent. 703 * The reference is registered with the queue for later purging. 704 * 705 * @param <T> the type of the referenced object 706 * @param type HARD, SOFT or WEAK 707 * @param referent the object to refer to 708 * @param hash the hash code of the <i>key</i> of the mapping; 709 * this number might be different from referent.hashCode() if 710 * the referent represents a value and not a key 711 * @return the reference to the object 712 */ 713 protected <T> Object toReference(final ReferenceStrength type, final T referent, final int hash) { 714 if (type == ReferenceStrength.HARD) { 715 return referent; 716 } 717 if (type == ReferenceStrength.SOFT) { 718 return new SoftRef<>(hash, referent, parent.queue); 719 } 720 if (type == ReferenceStrength.WEAK) { 721 return new WeakRef<>(hash, referent, parent.queue); 722 } 723 throw new Error(); 724 } 725 726 /** 727 * This is the callback for custom "after purge" logic 728 */ 729 protected void onPurge() { 730 } 731 732 /** 733 * Purges the specified reference 734 * @param ref the reference to purge 735 * @return true or false 736 */ 737 protected boolean purge(final Reference<?> ref) { 738 boolean r = parent.keyType != ReferenceStrength.HARD && key == ref; 739 r = r || parent.valueType != ReferenceStrength.HARD && value == ref; 740 if (r) { 741 if (parent.keyType != ReferenceStrength.HARD) { 742 ((Reference<?>) key).clear(); 743 } 744 if (parent.valueType != ReferenceStrength.HARD) { 745 ((Reference<?>) value).clear(); 746 } else if (parent.purgeValues) { 747 nullValue(); 748 } 749 } 750 return r; 751 } 752 753 /** 754 * Gets the next entry in the bucket. 755 * 756 * @return the next entry in the bucket 757 */ 758 protected ReferenceEntry<K, V> next() { 759 return (ReferenceEntry<K, V>) next; 760 } 761 762 /** 763 * This method can be overriden to provide custom logic to purge value 764 */ 765 protected void nullValue() { 766 value = null; 767 } 768 } 769 770 //----------------------------------------------------------------------- 771 /** 772 * Base iterator class. 773 */ 774 static class ReferenceBaseIterator<K, V> { 775 /** The parent map */ 776 final AbstractReferenceMap<K, V> parent; 777 778 // These fields keep track of where we are in the table. 779 int index; 780 ReferenceEntry<K, V> entry; 781 ReferenceEntry<K, V> previous; 782 783 // These Object fields provide hard references to the 784 // current and next entry; this assures that if hasNext() 785 // returns true, next() will actually return a valid element. 786 K currentKey, nextKey; 787 V currentValue, nextValue; 788 789 int expectedModCount; 790 791 public ReferenceBaseIterator(final AbstractReferenceMap<K, V> parent) { 792 super(); 793 this.parent = parent; 794 index = parent.size() != 0 ? parent.data.length : 0; 795 // have to do this here! size() invocation above 796 // may have altered the modCount. 797 expectedModCount = parent.modCount; 798 } 799 800 public boolean hasNext() { 801 checkMod(); 802 while (nextNull()) { 803 ReferenceEntry<K, V> e = entry; 804 int i = index; 805 while (e == null && i > 0) { 806 i--; 807 e = (ReferenceEntry<K, V>) parent.data[i]; 808 } 809 entry = e; 810 index = i; 811 if (e == null) { 812 currentKey = null; 813 currentValue = null; 814 return false; 815 } 816 nextKey = e.getKey(); 817 nextValue = e.getValue(); 818 if (nextNull()) { 819 entry = entry.next(); 820 } 821 } 822 return true; 823 } 824 825 private void checkMod() { 826 if (parent.modCount != expectedModCount) { 827 throw new ConcurrentModificationException(); 828 } 829 } 830 831 private boolean nextNull() { 832 return nextKey == null || nextValue == null; 833 } 834 835 protected ReferenceEntry<K, V> nextEntry() { 836 checkMod(); 837 if (nextNull() && !hasNext()) { 838 throw new NoSuchElementException(); 839 } 840 previous = entry; 841 entry = entry.next(); 842 currentKey = nextKey; 843 currentValue = nextValue; 844 nextKey = null; 845 nextValue = null; 846 return previous; 847 } 848 849 protected ReferenceEntry<K, V> currentEntry() { 850 checkMod(); 851 return previous; 852 } 853 854 public void remove() { 855 checkMod(); 856 if (previous == null) { 857 throw new IllegalStateException(); 858 } 859 parent.remove(currentKey); 860 previous = null; 861 currentKey = null; 862 currentValue = null; 863 expectedModCount = parent.modCount; 864 } 865 } 866 867 /** 868 * The EntrySet iterator. 869 */ 870 static class ReferenceEntrySetIterator<K, V> 871 extends ReferenceBaseIterator<K, V> implements Iterator<Map.Entry<K, V>> { 872 873 public ReferenceEntrySetIterator(final AbstractReferenceMap<K, V> parent) { 874 super(parent); 875 } 876 877 @Override 878 public Map.Entry<K, V> next() { 879 return nextEntry(); 880 } 881 882 } 883 884 /** 885 * The keySet iterator. 886 */ 887 static class ReferenceKeySetIterator<K> extends ReferenceBaseIterator<K, Object> implements Iterator<K> { 888 889 @SuppressWarnings("unchecked") 890 ReferenceKeySetIterator(final AbstractReferenceMap<K, ?> parent) { 891 super((AbstractReferenceMap<K, Object>) parent); 892 } 893 894 @Override 895 public K next() { 896 return nextEntry().getKey(); 897 } 898 } 899 900 /** 901 * The values iterator. 902 */ 903 static class ReferenceValuesIterator<V> extends ReferenceBaseIterator<Object, V> implements Iterator<V> { 904 905 @SuppressWarnings("unchecked") 906 ReferenceValuesIterator(final AbstractReferenceMap<?, V> parent) { 907 super((AbstractReferenceMap<Object, V>) parent); 908 } 909 910 @Override 911 public V next() { 912 return nextEntry().getValue(); 913 } 914 } 915 916 /** 917 * The MapIterator implementation. 918 */ 919 static class ReferenceMapIterator<K, V> extends ReferenceBaseIterator<K, V> implements MapIterator<K, V> { 920 921 protected ReferenceMapIterator(final AbstractReferenceMap<K, V> parent) { 922 super(parent); 923 } 924 925 @Override 926 public K next() { 927 return nextEntry().getKey(); 928 } 929 930 @Override 931 public K getKey() { 932 final HashEntry<K, V> current = currentEntry(); 933 if (current == null) { 934 throw new IllegalStateException(AbstractHashedMap.GETKEY_INVALID); 935 } 936 return current.getKey(); 937 } 938 939 @Override 940 public V getValue() { 941 final HashEntry<K, V> current = currentEntry(); 942 if (current == null) { 943 throw new IllegalStateException(AbstractHashedMap.GETVALUE_INVALID); 944 } 945 return current.getValue(); 946 } 947 948 @Override 949 public V setValue(final V value) { 950 final HashEntry<K, V> current = currentEntry(); 951 if (current == null) { 952 throw new IllegalStateException(AbstractHashedMap.SETVALUE_INVALID); 953 } 954 return current.setValue(value); 955 } 956 } 957 958 //----------------------------------------------------------------------- 959 // These two classes store the hashCode of the key of 960 // of the mapping, so that after they're dequeued a quick 961 // lookup of the bucket in the table can occur. 962 963 /** 964 * A soft reference holder. 965 */ 966 static class SoftRef<T> extends SoftReference<T> { 967 /** the hashCode of the key (even if the reference points to a value) */ 968 private final int hash; 969 970 public SoftRef(final int hash, final T r, final ReferenceQueue<? super T> q) { 971 super(r, q); 972 this.hash = hash; 973 } 974 975 @Override 976 public int hashCode() { 977 return hash; 978 } 979 } 980 981 /** 982 * A weak reference holder. 983 */ 984 static class WeakRef<T> extends WeakReference<T> { 985 /** the hashCode of the key (even if the reference points to a value) */ 986 private final int hash; 987 988 public WeakRef(final int hash, final T r, final ReferenceQueue<? super T> q) { 989 super(r, q); 990 this.hash = hash; 991 } 992 993 @Override 994 public int hashCode() { 995 return hash; 996 } 997 } 998 999 //----------------------------------------------------------------------- 1000 /** 1001 * Replaces the superclass method to store the state of this class. 1002 * <p> 1003 * Serialization is not one of the JDK's nicest topics. Normal serialization will 1004 * initialise the superclass before the subclass. Sometimes however, this isn't 1005 * what you want, as in this case the <code>put()</code> method on read can be 1006 * affected by subclass state. 1007 * <p> 1008 * The solution adopted here is to serialize the state data of this class in 1009 * this protected method. This method must be called by the 1010 * <code>writeObject()</code> of the first serializable subclass. 1011 * <p> 1012 * Subclasses may override if they have a specific field that must be present 1013 * on read before this implementation will work. Generally, the read determines 1014 * what must be serialized here, if anything. 1015 * 1016 * @param out the output stream 1017 * @throws IOException if an error occurs while writing to the stream 1018 */ 1019 @Override 1020 protected void doWriteObject(final ObjectOutputStream out) throws IOException { 1021 out.writeInt(keyType.value); 1022 out.writeInt(valueType.value); 1023 out.writeBoolean(purgeValues); 1024 out.writeFloat(loadFactor); 1025 out.writeInt(data.length); 1026 for (final MapIterator<K, V> it = mapIterator(); it.hasNext();) { 1027 out.writeObject(it.next()); 1028 out.writeObject(it.getValue()); 1029 } 1030 out.writeObject(null); // null terminate map 1031 // do not call super.doWriteObject() as code there doesn't work for reference map 1032 } 1033 1034 /** 1035 * Replaces the superclass method to read the state of this class. 1036 * <p> 1037 * Serialization is not one of the JDK's nicest topics. Normal serialization will 1038 * initialise the superclass before the subclass. Sometimes however, this isn't 1039 * what you want, as in this case the <code>put()</code> method on read can be 1040 * affected by subclass state. 1041 * <p> 1042 * The solution adopted here is to deserialize the state data of this class in 1043 * this protected method. This method must be called by the 1044 * <code>readObject()</code> of the first serializable subclass. 1045 * <p> 1046 * Subclasses may override if the subclass has a specific field that must be present 1047 * before <code>put()</code> or <code>calculateThreshold()</code> will work correctly. 1048 * 1049 * @param in the input stream 1050 * @throws IOException if an error occurs while reading from the stream 1051 * @throws ClassNotFoundException if an object read from the stream can not be loaded 1052 */ 1053 @Override 1054 @SuppressWarnings("unchecked") 1055 protected void doReadObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { 1056 this.keyType = ReferenceStrength.resolve(in.readInt()); 1057 this.valueType = ReferenceStrength.resolve(in.readInt()); 1058 this.purgeValues = in.readBoolean(); 1059 this.loadFactor = in.readFloat(); 1060 final int capacity = in.readInt(); 1061 init(); 1062 data = new HashEntry[capacity]; 1063 1064 // COLLECTIONS-599: Calculate threshold before populating, otherwise it will be 0 1065 // when it hits AbstractHashedMap.checkCapacity() and so will unnecessarily 1066 // double up the size of the "data" array during population. 1067 // 1068 // NB: AbstractHashedMap.doReadObject() DOES calculate the threshold before populating. 1069 // 1070 threshold = calculateThreshold(data.length, loadFactor); 1071 1072 while (true) { 1073 final K key = (K) in.readObject(); 1074 if (key == null) { 1075 break; 1076 } 1077 final V value = (V) in.readObject(); 1078 put(key, value); 1079 } 1080 // do not call super.doReadObject() as code there doesn't work for reference map 1081 } 1082 1083 /** 1084 * Provided protected read-only access to the key type. 1085 * @param type the type to check against. 1086 * @return true if keyType has the specified type 1087 */ 1088 protected boolean isKeyType(final ReferenceStrength type) { 1089 return this.keyType == type; 1090 } 1091 1092 /** 1093 * Provided protected read-only access to the value type. 1094 * @param type the type to check against. 1095 * @return true if valueType has the specified type 1096 */ 1097 protected boolean isValueType(final ReferenceStrength type) { 1098 return this.valueType == type; 1099 } 1100}