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