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