blob: b57f17d7d52ef453e9239c10df2b0ab986ab1c60 [file] [log] [blame]
Shuyi Chend7955ce2013-05-22 14:51:55 -07001// 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}