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