1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.collections.map;
18
19 import java.io.IOException;
20 import java.io.ObjectInputStream;
21 import java.io.ObjectOutputStream;
22 import java.lang.ref.Reference;
23 import java.lang.ref.ReferenceQueue;
24 import java.lang.ref.SoftReference;
25 import java.lang.ref.WeakReference;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.ConcurrentModificationException;
29 import java.util.Iterator;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.NoSuchElementException;
33 import java.util.Set;
34
35 import org.apache.commons.collections.MapIterator;
36 import org.apache.commons.collections.keyvalue.DefaultMapEntry;
37
38 /**
39 * An abstract implementation of a hash-based map that allows the entries to
40 * be removed by the garbage collector.
41 * <p>
42 * This class implements all the features necessary for a subclass reference
43 * hash-based map. Key-value entries are stored in instances of the
44 * <code>ReferenceEntry</code> class which can be overridden and replaced.
45 * The iterators can similarly be replaced, without the need to replace the KeySet,
46 * EntrySet and Values view classes.
47 * <p>
48 * Overridable methods are provided to change the default hashing behaviour, and
49 * to change how entries are added to and removed from the map. Hopefully, all you
50 * need for unusual subclasses is here.
51 * <p>
52 * When you construct an <code>AbstractReferenceMap</code>, you can specify what
53 * kind of references are used to store the map's keys and values.
54 * If non-hard references are used, then the garbage collector can remove
55 * mappings if a key or value becomes unreachable, or if the JVM's memory is
56 * running low. For information on how the different reference types behave,
57 * see {@link Reference}.
58 * <p>
59 * Different types of references can be specified for keys and values.
60 * The keys can be configured to be weak but the values hard,
61 * in which case this class will behave like a
62 * <a href="http://java.sun.com/j2se/1.4/docs/api/java/util/WeakHashMap.html">
63 * <code>WeakHashMap</code></a>. However, you can also specify hard keys and
64 * weak values, or any other combination. The default constructor uses
65 * hard keys and soft values, providing a memory-sensitive cache.
66 * <p>
67 * This {@link Map} implementation does <i>not</i> allow null elements.
68 * Attempting to add a null key or value to the map will raise a
69 * <code>NullPointerException</code>.
70 * <p>
71 * All the available iterators can be reset back to the start by casting to
72 * <code>ResettableIterator</code> and calling <code>reset()</code>.
73 * <p>
74 * This implementation is not synchronized.
75 * You can use {@link java.util.Collections#synchronizedMap} to
76 * provide synchronized access to a <code>ReferenceMap</code>.
77 *
78 * @see java.lang.ref.Reference
79 * @since 3.1 (extracted from ReferenceMap in 3.0)
80 * @version $Id: AbstractReferenceMap.java 1443538 2013-02-07 15:12:21Z tn $
81 */
82 public abstract class AbstractReferenceMap<K, V> extends AbstractHashedMap<K, V> {
83
84 /**
85 * Reference type enum.
86 */
87 public static enum ReferenceStrength {
88 HARD(0), SOFT(1), WEAK(2);
89
90 /** value */
91 public final int value;
92
93 /**
94 * Resolve enum from int.
95 * @param value the int value
96 * @return ReferenceType
97 * @throws IllegalArgumentException if the specified value is invalid.
98 */
99 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 protected ReferenceStrength keyType;
122
123 /**
124 * The reference type for values.
125 */
126 protected ReferenceStrength valueType;
127
128 /**
129 * Should the value be automatically purged when the associated key has been collected?
130 */
131 protected 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 protected 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 }