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