AbstractReferenceMap.java

  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.collections4.map;

  18. import java.io.IOException;
  19. import java.io.ObjectInputStream;
  20. import java.io.ObjectOutputStream;
  21. import java.lang.ref.Reference;
  22. import java.lang.ref.ReferenceQueue;
  23. import java.lang.ref.SoftReference;
  24. import java.lang.ref.WeakReference;
  25. import java.util.ArrayList;
  26. import java.util.Collection;
  27. import java.util.ConcurrentModificationException;
  28. import java.util.Iterator;
  29. import java.util.List;
  30. import java.util.Map;
  31. import java.util.NoSuchElementException;
  32. import java.util.Objects;
  33. import java.util.Set;

  34. import org.apache.commons.collections4.MapIterator;
  35. import org.apache.commons.collections4.keyvalue.DefaultMapEntry;

  36. /**
  37.  * An abstract implementation of a hash-based map that allows the entries to
  38.  * be removed by the garbage collector.
  39.  * <p>
  40.  * This class implements all the features necessary for a subclass reference
  41.  * hash-based map. Key-value entries are stored in instances of the
  42.  * {@code ReferenceEntry} class which can be overridden and replaced.
  43.  * The iterators can similarly be replaced, without the need to replace the KeySet,
  44.  * EntrySet and Values view classes.
  45.  * </p>
  46.  * <p>
  47.  * Overridable methods are provided to change the default hashing behavior, and
  48.  * to change how entries are added to and removed from the map. Hopefully, all you
  49.  * need for unusual subclasses is here.
  50.  * </p>
  51.  * <p>
  52.  * When you construct an {@code AbstractReferenceMap}, 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.  * <p>
  60.  * Different types of references can be specified for keys and values.
  61.  * The keys can be configured to be weak but the values hard,
  62.  * in which case this class will behave like a
  63.  * <a href="https://docs.oracle.com/javase/8/docs/api/java/util/WeakHashMap.html">
  64.  * {@code WeakHashMap}</a>. However, you can also specify hard keys and
  65.  * weak values, or any other combination. The default constructor uses
  66.  * hard keys and soft values, providing a memory-sensitive cache.
  67.  * </p>
  68.  * <p>
  69.  * This {@link Map} implementation does <em>not</em> allow null elements.
  70.  * Attempting to add a null key or value to the map will raise a
  71.  * {@code NullPointerException}.
  72.  * </p>
  73.  * <p>
  74.  * All the available iterators can be reset back to the start by casting to
  75.  * {@code ResettableIterator} and calling {@code reset()}.
  76.  * </p>
  77.  * <p>
  78.  * This implementation is not synchronized.
  79.  * You can use {@link java.util.Collections#synchronizedMap} to
  80.  * provide synchronized access to a {@code ReferenceMap}.
  81.  * </p>
  82.  *
  83.  * @param <K> the type of the keys in this map
  84.  * @param <V> the type of the values in this map
  85.  * @see java.lang.ref.Reference
  86.  * @since 3.1 (extracted from ReferenceMap in 3.0)
  87.  */
  88. public abstract class AbstractReferenceMap<K, V> extends AbstractHashedMap<K, V> {

  89.     /**
  90.      * Base iterator class.
  91.      */
  92.     static class ReferenceBaseIterator<K, V> {
  93.         /** The parent map */
  94.         final AbstractReferenceMap<K, V> parent;

  95.         // These fields keep track of where we are in the table.
  96.         int index;
  97.         ReferenceEntry<K, V> next;
  98.         ReferenceEntry<K, V> current;

  99.         // These Object fields provide hard references to the
  100.         // current and next entry; this assures that if hasNext()
  101.         // returns true, next() will actually return a valid element.
  102.         K currentKey;
  103.         K nextKey;
  104.         V currentValue;
  105.         V nextValue;

  106.         int expectedModCount;

  107.         ReferenceBaseIterator(final AbstractReferenceMap<K, V> parent) {
  108.             this.parent = parent;
  109.             index = !parent.isEmpty() ? parent.data.length : 0;
  110.             // have to do this here!  size() invocation above
  111.             // may have altered the modCount.
  112.             expectedModCount = parent.modCount;
  113.         }

  114.         private void checkMod() {
  115.             if (parent.modCount != expectedModCount) {
  116.                 throw new ConcurrentModificationException();
  117.             }
  118.         }

  119.         protected ReferenceEntry<K, V> currentEntry() {
  120.             checkMod();
  121.             return current;
  122.         }

  123.         public boolean hasNext() {
  124.             checkMod();
  125.             while (nextNull()) {
  126.                 ReferenceEntry<K, V> e = next;
  127.                 int i = index;
  128.                 while (e == null && i > 0) {
  129.                     i--;
  130.                     e = (ReferenceEntry<K, V>) parent.data[i];
  131.                 }
  132.                 next = e;
  133.                 index = i;
  134.                 if (e == null) {
  135.                     return false;
  136.                 }
  137.                 nextKey = e.getKey();
  138.                 nextValue = e.getValue();
  139.                 if (nextNull()) {
  140.                     next = next.next();
  141.                 }
  142.             }
  143.             return true;
  144.         }

  145.         protected ReferenceEntry<K, V> nextEntry() {
  146.             checkMod();
  147.             if (nextNull() && !hasNext()) {
  148.                 throw new NoSuchElementException();
  149.             }
  150.             current = next;
  151.             next = next.next();
  152.             currentKey = nextKey;
  153.             currentValue = nextValue;
  154.             nextKey = null;
  155.             nextValue = null;
  156.             return current;
  157.         }

  158.         private boolean nextNull() {
  159.             return nextKey == null || nextValue == null;
  160.         }

  161.         public void remove() {
  162.             checkMod();
  163.             if (current == null) {
  164.                 throw new IllegalStateException();
  165.             }
  166.             parent.remove(currentKey);
  167.             current = null;
  168.             currentKey = null;
  169.             currentValue = null;
  170.             expectedModCount = parent.modCount;
  171.         }
  172.     }

  173.     /**
  174.      * A MapEntry implementation for the map.
  175.      * <p>
  176.      * If getKey() or getValue() returns null, it means
  177.      * the mapping is stale and should be removed.
  178.      * </p>
  179.      *
  180.      * @param <K> the type of the keys
  181.      * @param <V> the type of the values
  182.      * @since 3.1
  183.      */
  184.     protected static class ReferenceEntry<K, V> extends HashEntry<K, V> {
  185.         /** The parent map */
  186.         private final AbstractReferenceMap<K, V> parent;

  187.         /**
  188.          * Creates a new entry object for the ReferenceMap.
  189.          *
  190.          * @param parent  the parent map
  191.          * @param next  the next entry in the hash bucket
  192.          * @param hashCode  the hash code of the key
  193.          * @param key  the key
  194.          * @param value  the value
  195.          */
  196.         public ReferenceEntry(final AbstractReferenceMap<K, V> parent, final HashEntry<K, V> next,
  197.                               final int hashCode, final K key, final V value) {
  198.             super(next, hashCode, null, null);
  199.             this.parent = parent;
  200.             this.key = toReference(parent.keyType, key, hashCode);
  201.             this.value = toReference(parent.valueType, value, hashCode); // the key hashCode is passed in deliberately
  202.         }

  203.         /**
  204.          * Compares this map entry to another.
  205.          * <p>
  206.          * This implementation uses {@code isEqualKey} and
  207.          * {@code isEqualValue} on the main map for comparison.
  208.          * </p>
  209.          *
  210.          * @param obj  the other map entry to compare to
  211.          * @return true if equal, false if not
  212.          */
  213.         @Override
  214.         public boolean equals(final Object obj) {
  215.             if (obj == this) {
  216.                 return true;
  217.             }
  218.             if (!(obj instanceof Map.Entry)) {
  219.                 return false;
  220.             }

  221.             final Map.Entry<?, ?> entry = (Map.Entry<?, ?>) obj;
  222.             final Object entryKey = entry.getKey();  // convert to hard reference
  223.             final Object entryValue = entry.getValue();  // convert to hard reference
  224.             if (entryKey == null || entryValue == null) {
  225.                 return false;
  226.             }
  227.             // compare using map methods, aiding identity subclass
  228.             // note that key is direct access and value is via method
  229.             return parent.isEqualKey(entryKey, key) &&
  230.                    parent.isEqualValue(entryValue, getValue());
  231.         }

  232.         /**
  233.          * Gets the key from the entry.
  234.          * This method dereferences weak and soft keys and thus may return null.
  235.          *
  236.          * @return the key, which may be null if it was garbage collected
  237.          */
  238.         @Override
  239.         @SuppressWarnings("unchecked")
  240.         public K getKey() {
  241.             return (K) (parent.keyType == ReferenceStrength.HARD ? key : ((Reference<K>) key).get());
  242.         }

  243.         /**
  244.          * Gets the value from the entry.
  245.          * This method dereferences weak and soft value and thus may return null.
  246.          *
  247.          * @return the value, which may be null if it was garbage collected
  248.          */
  249.         @Override
  250.         @SuppressWarnings("unchecked")
  251.         public V getValue() {
  252.             return (V) (parent.valueType == ReferenceStrength.HARD ? value : ((Reference<V>) value).get());
  253.         }

  254.         /**
  255.          * Gets the hash code of the entry using temporary hard references.
  256.          * <p>
  257.          * This implementation uses {@code hashEntry} on the main map.
  258.          *
  259.          * @return the hash code of the entry
  260.          */
  261.         @Override
  262.         public int hashCode() {
  263.             return parent.hashEntry(getKey(), getValue());
  264.         }

  265.         /**
  266.          * Gets the next entry in the bucket.
  267.          *
  268.          * @return the next entry in the bucket
  269.          */
  270.         protected ReferenceEntry<K, V> next() {
  271.             return (ReferenceEntry<K, V>) next;
  272.         }

  273.         /**
  274.          * This method can be overridden to provide custom logic to purge value
  275.          */
  276.         protected void nullValue() {
  277.             value = null;
  278.         }

  279.         /**
  280.          * This is the callback for custom "after purge" logic
  281.          */
  282.         protected void onPurge() {
  283.             // empty
  284.         }

  285.         /**
  286.          * Purges the specified reference
  287.          * @param ref  the reference to purge
  288.          * @return true or false
  289.          */
  290.         protected boolean purge(final Reference<?> ref) {
  291.             boolean r = parent.keyType != ReferenceStrength.HARD && key == ref;
  292.             r = r || parent.valueType != ReferenceStrength.HARD && value == ref;
  293.             if (r) {
  294.                 if (parent.keyType != ReferenceStrength.HARD) {
  295.                     ((Reference<?>) key).clear();
  296.                 }
  297.                 if (parent.valueType != ReferenceStrength.HARD) {
  298.                     ((Reference<?>) value).clear();
  299.                 } else if (parent.purgeValues) {
  300.                     nullValue();
  301.                 }
  302.             }
  303.             return r;
  304.         }

  305.         /**
  306.          * Sets the value of the entry.
  307.          *
  308.          * @param value  the object to store
  309.          * @return the previous value
  310.          */
  311.         @Override
  312.         @SuppressWarnings("unchecked")
  313.         public V setValue(final V value) {
  314.             final V old = getValue();
  315.             if (parent.valueType != ReferenceStrength.HARD) {
  316.                 ((Reference<V>) this.value).clear();
  317.             }
  318.             this.value = toReference(parent.valueType, value, hashCode);
  319.             return old;
  320.         }

  321.         /**
  322.          * Constructs a reference of the given type to the given referent.
  323.          * The reference is registered with the queue for later purging.
  324.          *
  325.          * @param <T> the type of the referenced object
  326.          * @param type  HARD, SOFT or WEAK
  327.          * @param referent  the object to refer to
  328.          * @param hash  the hash code of the <em>key</em> of the mapping;
  329.          *    this number might be different from referent.hashCode() if
  330.          *    the referent represents a value and not a key
  331.          * @return the reference to the object
  332.          */
  333.         protected <T> Object toReference(final ReferenceStrength type, final T referent, final int hash) {
  334.             switch (type) {
  335.             case HARD:
  336.                 return referent;
  337.             case SOFT:
  338.                 return new SoftRef<>(hash, referent, parent.queue);
  339.             case WEAK:
  340.                 return new WeakRef<>(hash, referent, parent.queue);
  341.             default:
  342.                 break;
  343.             }
  344.             throw new Error();
  345.         }
  346.     }

  347.     /**
  348.      * EntrySet implementation.
  349.      */
  350.     static class ReferenceEntrySet<K, V> extends EntrySet<K, V> {

  351.         protected ReferenceEntrySet(final AbstractHashedMap<K, V> parent) {
  352.             super(parent);
  353.         }

  354.         @Override
  355.         public Object[] toArray() {
  356.             return toArray(new Object[size()]);
  357.         }

  358.         @Override
  359.         public <T> T[] toArray(final T[] arr) {
  360.             // special implementation to handle disappearing entries
  361.             final ArrayList<Map.Entry<K, V>> list = new ArrayList<>(size());
  362.             for (final Map.Entry<K, V> entry : this) {
  363.                 list.add(new DefaultMapEntry<>(entry));
  364.             }
  365.             return list.toArray(arr);
  366.         }
  367.     }

  368.     /**
  369.      * The EntrySet iterator.
  370.      */
  371.     static class ReferenceEntrySetIterator<K, V>
  372.             extends ReferenceBaseIterator<K, V> implements Iterator<Map.Entry<K, V>> {

  373.         ReferenceEntrySetIterator(final AbstractReferenceMap<K, V> parent) {
  374.             super(parent);
  375.         }

  376.         @Override
  377.         public Map.Entry<K, V> next() {
  378.             return nextEntry();
  379.         }

  380.     }

  381.     /**
  382.      * KeySet implementation.
  383.      */
  384.     static class ReferenceKeySet<K> extends KeySet<K> {

  385.         protected ReferenceKeySet(final AbstractHashedMap<K, ?> parent) {
  386.             super(parent);
  387.         }

  388.         @Override
  389.         public Object[] toArray() {
  390.             return toArray(new Object[size()]);
  391.         }

  392.         @Override
  393.         public <T> T[] toArray(final T[] arr) {
  394.             // special implementation to handle disappearing keys
  395.             final List<K> list = new ArrayList<>(size());
  396.             forEach(list::add);
  397.             return list.toArray(arr);
  398.         }
  399.     }

  400.     /**
  401.      * The keySet iterator.
  402.      */
  403.     static class ReferenceKeySetIterator<K> extends ReferenceBaseIterator<K, Object> implements Iterator<K> {

  404.         @SuppressWarnings("unchecked")
  405.         ReferenceKeySetIterator(final AbstractReferenceMap<K, ?> parent) {
  406.             super((AbstractReferenceMap<K, Object>) parent);
  407.         }

  408.         @Override
  409.         public K next() {
  410.             return nextEntry().getKey();
  411.         }
  412.     }

  413.     /**
  414.      * The MapIterator implementation.
  415.      */
  416.     static class ReferenceMapIterator<K, V> extends ReferenceBaseIterator<K, V> implements MapIterator<K, V> {

  417.         protected ReferenceMapIterator(final AbstractReferenceMap<K, V> parent) {
  418.             super(parent);
  419.         }

  420.         @Override
  421.         public K getKey() {
  422.             final HashEntry<K, V> current = currentEntry();
  423.             if (current == null) {
  424.                 throw new IllegalStateException(GETKEY_INVALID);
  425.             }
  426.             return current.getKey();
  427.         }

  428.         @Override
  429.         public V getValue() {
  430.             final HashEntry<K, V> current = currentEntry();
  431.             if (current == null) {
  432.                 throw new IllegalStateException(GETVALUE_INVALID);
  433.             }
  434.             return current.getValue();
  435.         }

  436.         @Override
  437.         public K next() {
  438.             return nextEntry().getKey();
  439.         }

  440.         @Override
  441.         public V setValue(final V value) {
  442.             final HashEntry<K, V> current = currentEntry();
  443.             if (current == null) {
  444.                 throw new IllegalStateException(SETVALUE_INVALID);
  445.             }
  446.             return current.setValue(value);
  447.         }
  448.     }

  449.     /**
  450.      * Enumerates reference types.
  451.      */
  452.     public enum ReferenceStrength {

  453.         /**
  454.          * Hard reference type.
  455.          */
  456.         HARD(0),

  457.         /**
  458.          * Soft reference type.
  459.          */
  460.         SOFT(1),

  461.         /**
  462.          * Weak reference type.
  463.          */
  464.         WEAK(2);

  465.         /**
  466.          * Resolve enum from int.
  467.          * @param value  the int value
  468.          * @return ReferenceType
  469.          * @throws IllegalArgumentException if the specified value is invalid.
  470.          */
  471.         public static ReferenceStrength resolve(final int value) {
  472.             switch (value) {
  473.             case 0:
  474.                 return HARD;
  475.             case 1:
  476.                 return SOFT;
  477.             case 2:
  478.                 return WEAK;
  479.             default:
  480.                 throw new IllegalArgumentException();
  481.             }
  482.         }

  483.         /** Value */
  484.         public final int value;

  485.         ReferenceStrength(final int value) {
  486.             this.value = value;
  487.         }

  488.     }

  489.     /**
  490.      * Values implementation.
  491.      */
  492.     static class ReferenceValues<V> extends Values<V> {

  493.         protected ReferenceValues(final AbstractHashedMap<?, V> parent) {
  494.             super(parent);
  495.         }

  496.         @Override
  497.         public Object[] toArray() {
  498.             return toArray(new Object[size()]);
  499.         }

  500.         @Override
  501.         public <T> T[] toArray(final T[] arr) {
  502.             // special implementation to handle disappearing values
  503.             final List<V> list = new ArrayList<>(size());
  504.             forEach(list::add);
  505.             return list.toArray(arr);
  506.         }
  507.     }

  508.     /**
  509.      * The values iterator.
  510.      */
  511.     static class ReferenceValuesIterator<V> extends ReferenceBaseIterator<Object, V> implements Iterator<V> {

  512.         @SuppressWarnings("unchecked")
  513.         ReferenceValuesIterator(final AbstractReferenceMap<?, V> parent) {
  514.             super((AbstractReferenceMap<Object, V>) parent);
  515.         }

  516.         @Override
  517.         public V next() {
  518.             return nextEntry().getValue();
  519.         }
  520.     }

  521.     /**
  522.      * A soft reference holder.
  523.      */
  524.     static class SoftRef<T> extends SoftReference<T> {
  525.         /** The hashCode of the key (even if the reference points to a value) */
  526.         private final int hash;

  527.         SoftRef(final int hash, final T r, final ReferenceQueue<? super T> q) {
  528.             super(r, q);
  529.             this.hash = hash;
  530.         }

  531.         @Override
  532.         public boolean equals(final Object obj) {
  533.             if (this == obj) {
  534.                 return true;
  535.             }
  536.             if (obj == null) {
  537.                 return false;
  538.             }
  539.             if (getClass() != obj.getClass()) {
  540.                 return false;
  541.             }
  542.             final SoftRef<?> other = (SoftRef<?>) obj;
  543.             return hash == other.hash;
  544.         }

  545.         @Override
  546.         public int hashCode() {
  547.             return hash;
  548.         }
  549.     }

  550.     /**
  551.      * A weak reference holder.
  552.      */
  553.     static class WeakRef<T> extends WeakReference<T> {
  554.         /** The hashCode of the key (even if the reference points to a value) */
  555.         private final int hash;

  556.         WeakRef(final int hash, final T r, final ReferenceQueue<? super T> q) {
  557.             super(r, q);
  558.             this.hash = hash;
  559.         }

  560.         @Override
  561.         public boolean equals(final Object obj) {
  562.             if (this == obj) {
  563.                 return true;
  564.             }
  565.             if (obj == null) {
  566.                 return false;
  567.             }
  568.             if (getClass() != obj.getClass()) {
  569.                 return false;
  570.             }
  571.             final WeakRef<?> other = (WeakRef<?>) obj;
  572.             return hash == other.hash;
  573.         }

  574.         @Override
  575.         public int hashCode() {
  576.             return hash;
  577.         }
  578.     }

  579.     /**
  580.      * The reference type for keys.
  581.      */
  582.     private ReferenceStrength keyType;

  583.     /**
  584.      * The reference type for values.
  585.      */
  586.     private ReferenceStrength valueType;

  587.     /**
  588.      * Should the value be automatically purged when the associated key has been collected?
  589.      */
  590.     private boolean purgeValues;

  591.     /**
  592.      * ReferenceQueue used to eliminate stale mappings.
  593.      * See purge.
  594.      */
  595.     private transient ReferenceQueue<Object> queue;

  596.     /**
  597.      * Constructor used during deserialization.
  598.      */
  599.     protected AbstractReferenceMap() {
  600.     }

  601.     /**
  602.      * Constructs a new empty map with the specified reference types,
  603.      * load factor and initial capacity.
  604.      *
  605.      * @param keyType  the type of reference to use for keys;
  606.      *   must be {@link ReferenceStrength#HARD HARD},
  607.      *   {@link ReferenceStrength#SOFT SOFT},
  608.      *   {@link ReferenceStrength#WEAK WEAK}
  609.      * @param valueType  the type of reference to use for values;
  610.      *   must be {@link ReferenceStrength#HARD},
  611.      *   {@link ReferenceStrength#SOFT SOFT},
  612.      *   {@link ReferenceStrength#WEAK WEAK}
  613.      * @param capacity  the initial capacity for the map
  614.      * @param loadFactor  the load factor for the map
  615.      * @param purgeValues  should the value be automatically purged when the
  616.      *   key is garbage collected
  617.      */
  618.     protected AbstractReferenceMap(
  619.             final ReferenceStrength keyType, final ReferenceStrength valueType, final int capacity,
  620.             final float loadFactor, final boolean purgeValues) {
  621.         super(capacity, loadFactor);
  622.         this.keyType = keyType;
  623.         this.valueType = valueType;
  624.         this.purgeValues = purgeValues;
  625.     }

  626.     /**
  627.      * Clears this map.
  628.      */
  629.     @Override
  630.     public void clear() {
  631.         super.clear();
  632.         // Drain the queue
  633.         while (queue.poll() != null) { // NOPMD
  634.         }
  635.     }

  636.     /**
  637.      * Checks whether the map contains the specified key.
  638.      *
  639.      * @param key  the key to search for
  640.      * @return true if the map contains the key
  641.      */
  642.     @Override
  643.     public boolean containsKey(final Object key) {
  644.         purgeBeforeRead();
  645.         final Entry<K, V> entry = getEntry(key);
  646.         if (entry == null) {
  647.             return false;
  648.         }
  649.         return entry.getValue() != null;
  650.     }

  651.     /**
  652.      * Checks whether the map contains the specified value.
  653.      *
  654.      * @param value  the value to search for
  655.      * @return true if the map contains the value
  656.      */
  657.     @Override
  658.     public boolean containsValue(final Object value) {
  659.         purgeBeforeRead();
  660.         if (value == null) {
  661.             return false;
  662.         }
  663.         return super.containsValue(value);
  664.     }

  665.     /**
  666.      * Creates a ReferenceEntry instead of a HashEntry.
  667.      *
  668.      * @param next  the next entry in sequence
  669.      * @param hashCode  the hash code to use
  670.      * @param key  the key to store
  671.      * @param value  the value to store
  672.      * @return the newly created entry
  673.      */
  674.     @Override
  675.     protected ReferenceEntry<K, V> createEntry(final HashEntry<K, V> next, final int hashCode,
  676.                                                final K key, final V value) {
  677.         return new ReferenceEntry<>(this, next, hashCode, key, value);
  678.     }

  679.     /**
  680.      * Creates an entry set iterator.
  681.      *
  682.      * @return the entrySet iterator
  683.      */
  684.     @Override
  685.     protected Iterator<Map.Entry<K, V>> createEntrySetIterator() {
  686.         return new ReferenceEntrySetIterator<>(this);
  687.     }

  688.     /**
  689.      * Creates a key set iterator.
  690.      *
  691.      * @return the keySet iterator
  692.      */
  693.     @Override
  694.     protected Iterator<K> createKeySetIterator() {
  695.         return new ReferenceKeySetIterator<>(this);
  696.     }

  697.     /**
  698.      * Creates a values iterator.
  699.      *
  700.      * @return the values iterator
  701.      */
  702.     @Override
  703.     protected Iterator<V> createValuesIterator() {
  704.         return new ReferenceValuesIterator<>(this);
  705.     }

  706.     /**
  707.      * Replaces the superclass method to read the state of this class.
  708.      * <p>
  709.      * Serialization is not one of the JDK's nicest topics. Normal serialization will
  710.      * initialize the superclass before the subclass. Sometimes however, this isn't
  711.      * what you want, as in this case the {@code put()} method on read can be
  712.      * affected by subclass state.
  713.      * </p>
  714.      * <p>
  715.      * The solution adopted here is to deserialize the state data of this class in
  716.      * this protected method. This method must be called by the
  717.      * {@code readObject()} of the first serializable subclass.
  718.      * </p>
  719.      * <p>
  720.      * Subclasses may override if the subclass has a specific field that must be present
  721.      * before {@code put()} or {@code calculateThreshold()} will work correctly.
  722.      * </p>
  723.      *
  724.      * @param in  the input stream
  725.      * @throws IOException if an error occurs while reading from the stream
  726.      * @throws ClassNotFoundException if an object read from the stream cannot be loaded
  727.      */
  728.     @Override
  729.     @SuppressWarnings("unchecked")
  730.     protected void doReadObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
  731.         keyType = ReferenceStrength.resolve(in.readInt());
  732.         valueType = ReferenceStrength.resolve(in.readInt());
  733.         purgeValues = in.readBoolean();
  734.         loadFactor = in.readFloat();
  735.         final int capacity = in.readInt();
  736.         init();
  737.         data = new HashEntry[capacity];

  738.         // COLLECTIONS-599: Calculate threshold before populating, otherwise it will be 0
  739.         // when it hits AbstractHashedMap.checkCapacity() and so will unnecessarily
  740.         // double up the size of the "data" array during population.
  741.         //
  742.         // NB: AbstractHashedMap.doReadObject() DOES calculate the threshold before populating.
  743.         //
  744.         threshold = calculateThreshold(data.length, loadFactor);

  745.         while (true) {
  746.             final K key = (K) in.readObject();
  747.             if (key == null) {
  748.                 break;
  749.             }
  750.             final V value = (V) in.readObject();
  751.             put(key, value);
  752.         }
  753.         // do not call super.doReadObject() as code there doesn't work for reference map
  754.     }

  755.     /**
  756.      * Replaces the superclass method to store the state of this class.
  757.      * <p>
  758.      * Serialization is not one of the JDK's nicest topics. Normal serialization will
  759.      * initialize the superclass before the subclass. Sometimes however, this isn't
  760.      * what you want, as in this case the {@code put()} method on read can be
  761.      * affected by subclass state.
  762.      * </p>
  763.      * <p>
  764.      * The solution adopted here is to serialize the state data of this class in
  765.      * this protected method. This method must be called by the
  766.      * {@code writeObject()} of the first serializable subclass.
  767.      * </p>
  768.      * <p>
  769.      * Subclasses may override if they have a specific field that must be present
  770.      * on read before this implementation will work. Generally, the read determines
  771.      * what must be serialized here, if anything.
  772.      * </p>
  773.      *
  774.      * @param out  the output stream
  775.      * @throws IOException if an error occurs while writing to the stream
  776.      */
  777.     @Override
  778.     protected void doWriteObject(final ObjectOutputStream out) throws IOException {
  779.         out.writeInt(keyType.value);
  780.         out.writeInt(valueType.value);
  781.         out.writeBoolean(purgeValues);
  782.         out.writeFloat(loadFactor);
  783.         out.writeInt(data.length);
  784.         for (final MapIterator<K, V> it = mapIterator(); it.hasNext();) {
  785.             out.writeObject(it.next());
  786.             out.writeObject(it.getValue());
  787.         }
  788.         out.writeObject(null);  // null terminate map
  789.         // do not call super.doWriteObject() as code there doesn't work for reference map
  790.     }

  791.     /**
  792.      * Returns a set view of this map's entries.
  793.      * An iterator returned entry is valid until {@code next()} is called again.
  794.      * The {@code setValue()} method on the {@code toArray} entries has no effect.
  795.      *
  796.      * @return a set view of this map's entries
  797.      */
  798.     @Override
  799.     public Set<Map.Entry<K, V>> entrySet() {
  800.         if (entrySet == null) {
  801.             entrySet = new ReferenceEntrySet<>(this);
  802.         }
  803.         return entrySet;
  804.     }

  805.     /**
  806.      * Gets the value mapped to the key specified.
  807.      *
  808.      * @param key  the key
  809.      * @return the mapped value, null if no match
  810.      */
  811.     @Override
  812.     public V get(final Object key) {
  813.         purgeBeforeRead();
  814.         final Entry<K, V> entry = getEntry(key);
  815.         if (entry == null) {
  816.             return null;
  817.         }
  818.         return entry.getValue();
  819.     }

  820.     /**
  821.      * Gets the entry mapped to the key specified.
  822.      *
  823.      * @param key  the key
  824.      * @return the entry, null if no match
  825.      */
  826.     @Override
  827.     protected HashEntry<K, V> getEntry(final Object key) {
  828.         if (key == null) {
  829.             return null;
  830.         }
  831.         return super.getEntry(key);
  832.     }

  833.     /**
  834.      * Gets the hash code for a MapEntry.
  835.      * Subclasses can override this, for example to use the identityHashCode.
  836.      *
  837.      * @param key  the key to get a hash code for, may be null
  838.      * @param value  the value to get a hash code for, may be null
  839.      * @return the hash code, as per the MapEntry specification
  840.      */
  841.     protected int hashEntry(final Object key, final Object value) {
  842.         return (key == null ? 0 : key.hashCode()) ^
  843.                (value == null ? 0 : value.hashCode());
  844.     }

  845.     /**
  846.      * Initialize this subclass during construction, cloning or deserialization.
  847.      */
  848.     @Override
  849.     protected void init() {
  850.         queue = new ReferenceQueue<>();
  851.     }

  852.     /**
  853.      * Checks whether the map is currently empty.
  854.      *
  855.      * @return true if the map is currently size zero
  856.      */
  857.     @Override
  858.     public boolean isEmpty() {
  859.         purgeBeforeRead();
  860.         return super.isEmpty();
  861.     }

  862.     /**
  863.      * Compares two keys, in internal converted form, to see if they are equal.
  864.      * <p>
  865.      * This implementation converts the key from the entry to a real reference
  866.      * before comparison.
  867.      * </p>
  868.      *
  869.      * @param key1  the first key to compare passed in from outside
  870.      * @param key2  the second key extracted from the entry via {@code entry.key}
  871.      * @return true if equal
  872.      */
  873.     @Override
  874.     @SuppressWarnings("unchecked")
  875.     protected boolean isEqualKey(final Object key1, Object key2) {
  876.         key2 = keyType == ReferenceStrength.HARD ? key2 : ((Reference<K>) key2).get();
  877.         return Objects.equals(key1, key2);
  878.     }

  879.     /**
  880.      * Provided protected read-only access to the key type.
  881.      *
  882.      * @param type the type to check against.
  883.      * @return true if keyType has the specified type
  884.      */
  885.     protected boolean isKeyType(final ReferenceStrength type) {
  886.         return keyType == type;
  887.     }

  888.     /**
  889.      * Provided protected read-only access to the value type.
  890.      *
  891.      * @param type the type to check against.
  892.      * @return true if valueType has the specified type
  893.      */
  894.     protected boolean isValueType(final ReferenceStrength type) {
  895.         return valueType == type;
  896.     }

  897.     /**
  898.      * Returns a set view of this map's keys.
  899.      *
  900.      * @return a set view of this map's keys
  901.      */
  902.     @Override
  903.     public Set<K> keySet() {
  904.         if (keySet == null) {
  905.             keySet = new ReferenceKeySet<>(this);
  906.         }
  907.         return keySet;
  908.     }

  909.     /**
  910.      * Gets a MapIterator over the reference map.
  911.      * The iterator only returns valid key/value pairs.
  912.      *
  913.      * @return a map iterator
  914.      */
  915.     @Override
  916.     public MapIterator<K, V> mapIterator() {
  917.         return new ReferenceMapIterator<>(this);
  918.     }

  919.     /**
  920.      * Purges stale mappings from this map.
  921.      * <p>
  922.      * Note that this method is not synchronized!  Special
  923.      * care must be taken if, for instance, you want stale
  924.      * mappings to be removed on a periodic basis by some
  925.      * background thread.
  926.      * </p>
  927.      */
  928.     protected void purge() {
  929.         Reference<?> ref = queue.poll();
  930.         while (ref != null) {
  931.             purge(ref);
  932.             ref = queue.poll();
  933.         }
  934.     }

  935.     /**
  936.      * Purges the specified reference.
  937.      *
  938.      * @param ref  the reference to purge
  939.      */
  940.     protected void purge(final Reference<?> ref) {
  941.         // The hashCode of the reference is the hashCode of the
  942.         // mapping key, even if the reference refers to the
  943.         // mapping value...
  944.         final int hash = ref.hashCode();
  945.         final int index = hashIndex(hash, data.length);
  946.         HashEntry<K, V> previous = null;
  947.         HashEntry<K, V> entry = data[index];
  948.         while (entry != null) {
  949.             final ReferenceEntry<K, V> refEntry = (ReferenceEntry<K, V>) entry;
  950.             if (refEntry.purge(ref)) {
  951.                 if (previous == null) {
  952.                     data[index] = entry.next;
  953.                 } else {
  954.                     previous.next = entry.next;
  955.                 }
  956.                 size--;
  957.                 refEntry.onPurge();
  958.                 return;
  959.             }
  960.             previous = entry;
  961.             entry = entry.next;
  962.         }

  963.     }

  964.     // These two classes store the hashCode of the key of
  965.     // the mapping, so that after they're dequeued a quick
  966.     // lookup of the bucket in the table can occur.

  967.     /**
  968.      * Purges stale mappings from this map before read operations.
  969.      * <p>
  970.      * This implementation calls {@link #purge()} to maintain a consistent state.
  971.      */
  972.     protected void purgeBeforeRead() {
  973.         purge();
  974.     }

  975.     /**
  976.      * Purges stale mappings from this map before write operations.
  977.      * <p>
  978.      * This implementation calls {@link #purge()} to maintain a consistent state.
  979.      * </p>
  980.      */
  981.     protected void purgeBeforeWrite() {
  982.         purge();
  983.     }

  984.     /**
  985.      * Puts a key-value mapping into this map.
  986.      * Neither the key nor the value may be null.
  987.      *
  988.      * @param key  the key to add, must not be null
  989.      * @param value  the value to add, must not be null
  990.      * @return the value previously mapped to this key, null if none
  991.      * @throws NullPointerException if either the key or value is null
  992.      */
  993.     @Override
  994.     public V put(final K key, final V value) {
  995.         Objects.requireNonNull(key, "key");
  996.         Objects.requireNonNull(value, "value");
  997.         purgeBeforeWrite();
  998.         return super.put(key, value);
  999.     }

  1000.     /**
  1001.      * Removes the specified mapping from this map.
  1002.      *
  1003.      * @param key  the mapping to remove
  1004.      * @return the value mapped to the removed key, null if key not in map
  1005.      */
  1006.     @Override
  1007.     public V remove(final Object key) {
  1008.         if (key == null) {
  1009.             return null;
  1010.         }
  1011.         purgeBeforeWrite();
  1012.         return super.remove(key);
  1013.     }

  1014.     /**
  1015.      * Gets the size of the map.
  1016.      *
  1017.      * @return the size
  1018.      */
  1019.     @Override
  1020.     public int size() {
  1021.         purgeBeforeRead();
  1022.         return super.size();
  1023.     }

  1024.     /**
  1025.      * Returns a collection view of this map's values.
  1026.      *
  1027.      * @return a set view of this map's values
  1028.      */
  1029.     @Override
  1030.     public Collection<V> values() {
  1031.         if (values == null) {
  1032.             values = new ReferenceValues<>(this);
  1033.         }
  1034.         return values;
  1035.     }
  1036. }