View Javadoc

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 }