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