/*
 * Written by Doug Lea with assistance from members of JCP JSR-166
 * Expert Group and released to the public domain, as explained at
 * http://creativecommons.org/publicdomain/zero/1.0/
 */

/*
 * The initial version of this file was copied from JSR-166:
 * http://gee.cs.oswego.edu/dl/concurrency-interest/
 */

package org.jsr166;

import java.util.AbstractCollection;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.LongAdder;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.ignite.internal.util.tostring.GridToStringExclude;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.jetbrains.annotations.Nullable;

import static org.jsr166.ConcurrentLinkedHashMap.QueuePolicy.PER_SEGMENT_Q;
import static org.jsr166.ConcurrentLinkedHashMap.QueuePolicy.PER_SEGMENT_Q_OPTIMIZED_RMV;
import static org.jsr166.ConcurrentLinkedHashMap.QueuePolicy.SINGLE_Q;

/**
 * A hash table supporting full concurrency of retrievals and
 * adjustable expected concurrency for updates. This class obeys the
 * same functional specification as {@link java.util.Hashtable}, and
 * includes versions of methods corresponding to each method of
 * <tt>Hashtable</tt>. However, even though all operations are
 * thread-safe, retrieval operations do <em>not</em> entail locking,
 * and there is <em>not</em> any support for locking the entire table
 * in a way that prevents all access.  This class is fully
 * interoperable with <tt>Hashtable</tt> in programs that rely on its
 * thread safety but not on its synchronization details.
 *
 * <p> Retrieval operations (including <tt>get</tt>) generally do not
 * block, so may overlap with update operations (including
 * <tt>put</tt> and <tt>remove</tt>). Retrievals reflect the results
 * of the most recently <em>completed</em> update operations holding
 * upon their onset.  For aggregate operations such as <tt>putAll</tt>
 * and <tt>clear</tt>, concurrent retrievals may reflect insertion or
 * removal of only some entries.  Similarly, Iterators and
 * Enumerations return elements reflecting the state of the hash table
 * at some point at or since the creation of the iterator/enumeration.
 * They do <em>not</em> throw {@link ConcurrentModificationException}.
 * However, iterators are designed to be used by only one thread at a time.
 *
 * <p> The allowed concurrency among update operations is guided by
 * the optional <tt>concurrencyLevel</tt> constructor argument
 * (default <tt>16</tt>), which is used as a hint for internal sizing.  The
 * table is internally partitioned to try to permit the indicated
 * number of concurrent updates without contention. Because placement
 * in hash tables is essentially random, the actual concurrency will
 * vary.  Ideally, you should choose a value to accommodate as many
 * threads as will ever concurrently modify the table. Using a
 * significantly higher value than you need can waste space and time,
 * and a significantly lower value can lead to thread contention. But
 * overestimates and underestimates within an order of magnitude do
 * not usually have much noticeable impact. A value of one is
 * appropriate when it is known that only one thread will modify and
 * all others will only read. Also, resizing this or any other kind of
 * hash table is a relatively slow operation, so, when possible, it is
 * a good idea to provide estimates of expected table sizes in
 * constructors.
 *
 * <p/> This implementation differs from
 * <tt>HashMap</tt> in that it maintains a doubly-linked list running through
 * all of its entries.  This linked list defines the iteration ordering,
 * which is the order in which keys were inserted into the map
 * (<i>insertion-order</i>).
 *
 * <p> NOTE: Access order is not supported by this map.
 *
 * Note that insertion order is not affected
 * if a key is <i>re-inserted</i> into the map. (A key <tt>k</tt> is
 * reinserted into a map <tt>m</tt> if <tt>m.put(k, v)</tt> is invoked when
 * <tt>m.containsKey(k)</tt> would return <tt>true</tt> immediately prior to
 * the invocation.)
 *
 * <p>An optional {@code maxCap} may be passed to the map constructor to
 * create bounded map that will remove stale mappings automatically when new mappings
 * are added to the map.
 *
 * <p/>When iterating over the key set in insertion order one should note that iterator
 * will see all removes done since the iterator was created, but will see <b>no</b>
 * inserts to map.
 *
 * <p>This class and its views and iterators implement all of the
 * <em>optional</em> methods of the {@link Map} and {@link Iterator}
 * interfaces.
 *
 * <p> Like {@link Hashtable} but unlike {@link HashMap}, this class
 * does <em>not</em> allow <tt>null</tt> to be used as a key or value.
 *
 * @author Doug Lea
 * @param <K> the type of keys maintained by this map
 * @param <V> the type of mapped values
 */
public class ConcurrentLinkedHashMap<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> {
    /*
     * The basic strategy is to subdivide the table among Segments,
     * each of which itself is a concurrently readable hash table.
     */

    /* ---------------- Constants -------------- */

    /**
     * The default initial capacity for this table,
     * used when not otherwise specified in a constructor.
     */
    public static final int DFLT_INIT_CAP = 16;

    /**
     * The default load factor for this table, used when not
     * otherwise specified in a constructor.
     */
    public static final float DFLT_LOAD_FACTOR = 0.75f;

    /**
     * The default concurrency level for this table, used when not
     * otherwise specified in a constructor.
     */
    public static final int DFLT_CONCUR_LVL = 16;

    /**
     * The maximum capacity, used if a higher value is implicitly
     * specified by either of the constructors with arguments.  MUST
     * be a power of two <= 1<<30 to ensure that entries are indexable
     * using ints.
     */
    public static final int MAX_CAP_LIMIT = 1 << 30;

    /**
     * The maximum number of segments to allow; used to bound
     * constructor arguments.
     */
    public static final int MAX_SEGS = 1 << 16; // slightly conservative

    /**
     * Number of unsynchronized retries in {@link #size} and {@link #containsValue}
     * methods before resorting to locking. This is used to avoid
     * unbounded retries if tables undergo continuous modification
     * which would make it impossible to obtain an accurate result.
     */
    public static final int RETRIES_BEFORE_LOCK = 2;

    /* ---------------- Fields -------------- */

    /**
     * Mask value for indexing into segments. The upper bits of a
     * key's hash code are used to choose the segment.
     */
    private final int segmentMask;

    /** Shift value for indexing within segments. */
    private final int segmentShift;

    /** The segments, each of which is a specialized hash table. */
    private final Segment<K, V>[] segments;

    /** Key set. */
    private Set<K> keySet;

    /** Key set. */
    private Set<K> descKeySet;

    /** Entry set */
    private Set<Map.Entry<K, V>> entrySet;

    /** Entry set in descending order. */
    private Set<Map.Entry<K, V>> descEntrySet;

    /** Values collection. */
    private Collection<V> vals;

    /** Values collection in descending order. */
    private Collection<V> descVals;

    /** Queue containing order of entries. */
    private final ConcurrentLinkedDeque8<HashEntry<K, V>> entryQ;

    /** Atomic variable containing map size. */
    private final LongAdder size = new LongAdder();

    /** */
    private final LongAdder modCnt = new LongAdder();

    /** */
    private final int maxCap;

    /** */
    private final QueuePolicy qPlc;

    /* ---------------- Small Utilities -------------- */

    /**
     * Applies a supplemental hash function to a given hashCode, which
     * defends against poor quality hash functions.  This is critical
     * because ConcurrentHashMap uses power-of-two length hash tables,
     * that otherwise encounter collisions for hashCodes that do not
     * differ in lower or upper bits.
     *
     * @param h Input hash.
     * @return Hash.
     */
    private static int hash(int h) {
        // Apply base step of MurmurHash; see http://code.google.com/p/smhasher/
        // Despite two multiplies, this is often faster than others
        // with comparable bit-spread properties.
        h ^= h >>> 16;
        h *= 0x85ebca6b;
        h ^= h >>> 13;
        h *= 0xc2b2ae35;

        return ((h >>> 16) ^ h);
    }

    /**
     * Returns the segment that should be used for key with given hash.
     *
     * @param hash the hash code for the key
     * @return the segment
     */
    private Segment<K, V> segmentFor(int hash) {
        return segments[(hash >>> segmentShift) & segmentMask];
    }

    /* ---------------- Inner Classes -------------- */

    /**
     * ConcurrentHashMap list entry. Note that this is never exported
     * out as a user-visible Map.Entry.
     *
     * Because the value field is volatile, not final, it is legal wrt
     * the Java Memory Model for an unsynchronized reader to see null
     * instead of initial value when read via a data race.  Although a
     * reordering leading to this is not likely to ever actually
     * occur, the Segment.readValueUnderLock method is used as a
     * snapshot in case a null (pre-initialized) value is ever seen in
     * an unsynchronized access method.
     */
    @SuppressWarnings({"PublicInnerClass"})
    public static final class HashEntry<K, V> {
        /** Key. */
        private final K key;

        /** Hash of the key after {@code hash()} method is applied. */
        private final int hash;

        /** Value. */
        private volatile V val;

        /** Reference to a node in queue for fast removal operations. */
        @GridToStringExclude
        private volatile ConcurrentLinkedDeque8.Node node;

        /** Modification count of the map for duplicates exclusion. */
        private volatile int modCnt;

        /** Link to the next entry in a bucket */
        @GridToStringExclude
        private final HashEntry<K, V> next;

        /**
         * @param key Key.
         * @param hash Key hash.
         * @param next Link to next.
         * @param val Value.
         */
        HashEntry(K key, int hash, HashEntry<K, V> next, V val) {
            this.key = key;
            this.hash = hash;
            this.next = next;
            this.val = val;
        }

        /**
         * @param key Key.
         * @param hash Key hash.
         * @param next Link to next.
         * @param val Value.
         * @param node Queue node.
         * @param modCnt Mod count.
         */
        HashEntry(K key, int hash, HashEntry<K, V> next, V val, ConcurrentLinkedDeque8.Node node, int modCnt) {
            this.key = key;
            this.hash = hash;
            this.next = next;
            this.val = val;
            this.node = node;
            this.modCnt = modCnt;
        }

        /**
         * Returns key of this entry.
         *
         * @return Key.
         */
        public K getKey() {
            return key;
        }

        /**
         * Return value of this entry.
         *
         * @return Value.
         */
        public V getValue() {
            return val;
        }

        /**
         * Creates new array of entries.
         *
         * @param i Size of array.
         * @param <K> Key type.
         * @param <V> Value type.
         * @return Empty array.
         */
        @SuppressWarnings("unchecked")
        static <K, V> HashEntry<K, V>[] newArray(int i) {
            return new HashEntry[i];
        }

        /** {@inheritDoc} */
        @Override public String toString() {
            return S.toString(HashEntry.class, this, "key", key, "val", val);
        }
    }

    /**
     * Segments are specialized versions of hash tables.  This
     * subclasses from ReentrantLock opportunistically, just to
     * simplify some locking and avoid separate construction.
     */
    @SuppressWarnings({"TransientFieldNotInitialized"})
    private final class Segment<K, V> extends ReentrantReadWriteLock {
        /*
         * Segments maintain a table of entry lists that are ALWAYS
         * kept in a consistent state, so can be read without locking.
         * Next fields of nodes are immutable (final).  All list
         * additions are performed at the front of each bin. This
         * makes it easy to check changes, and also fast to traverse.
         * When nodes would otherwise be changed, new nodes are
         * created to replace them. This works well for hash tables
         * since the bin lists tend to be short. (The average length
         * is less than two for the default load factor threshold.)
         *
         * Read operations can thus proceed without locking, but rely
         * on selected uses of volatiles to ensure that completed
         * write operations performed by other threads are
         * noticed. For most purposes, the "count" field, tracking the
         * number of elements, serves as that volatile variable
         * ensuring visibility.  This is convenient because this field
         * needs to be read in many read operations anyway:
         *
         *   - All (unsynchronized) read operations must first read the
         *     "count" field, and should not look at table entries if
         *     it is 0.
         *
         *   - All (synchronized) write operations should write to
         *     the "count" field after structurally changing any bin.
         *     The operations must not take any action that could even
         *     momentarily cause a concurrent read operation to see
         *     inconsistent data. This is made easier by the nature of
         *     the read operations in Map. For example, no operation
         *     can reveal that the table has grown but the threshold
         *     has not yet been updated, so there are no atomicity
         *     requirements for this with respect to reads.
         *
         * As a guide, all critical volatile reads and writes to the
         * count field are marked in code comments.
         */

        /** The number of elements in this segment's region. */
        private transient volatile int cnt;

        /**
         * Number of updates that alter the size of the table. This is
         * used during bulk-read methods to make sure they see a
         * consistent snapshot: If modCounts change during a traversal
         * of segments computing size or checking containsValue, then
         * we might have an inconsistent view of state so (usually)
         * must retry.
         */
        private transient int modCnt;

        /**
         * The table is rehashed when its size exceeds this threshold.
         * (The value of this field is always <tt>(int)(capacity *
         * loadFactor)</tt>.)
         */
        private transient int threshold;

        /** The per-segment table. */
        private transient volatile HashEntry<K, V>[] tbl;

        /**
         * The load factor for the hash table.  Even though this value
         * is same for all segments, it is replicated to avoid needing
         * links to outer object.
         */
        private final float loadFactor;

        /** */
        private final Queue<HashEntry<K, V>> segEntryQ;

        /**
         * @param initCap Segment initial capacity.
         * @param loadFactor Segment load factor,
         */
        Segment(int initCap, float loadFactor) {
            this.loadFactor = loadFactor;

            segEntryQ = qPlc == PER_SEGMENT_Q ? new ArrayDeque<HashEntry<K, V>>() :
                qPlc == PER_SEGMENT_Q_OPTIMIZED_RMV ? new ConcurrentLinkedDeque8<HashEntry<K, V>>() : null;

            setTable(HashEntry.<K, V>newArray(initCap));
        }

        /**
         * Sets table to new HashEntry array.
         * Call only while holding lock or in constructor.
         *
         * @param newTbl New hash table
         */
        void setTable(HashEntry<K, V>[] newTbl) {
            threshold = (int)(newTbl.length * loadFactor);
            tbl = newTbl;
        }

        /**
         * Returns properly casted first entry of bin for given hash.
         *
         * @param hash Hash of the key.
         * @return Head of bin's linked list.
         */
        HashEntry<K, V> getFirst(int hash) {
            HashEntry<K, V>[] tab = tbl;

            return tab[hash & (tab.length - 1)];
        }

        /**
         * Reads value field of an entry under lock. Called if value
         * field ever appears to be null. This is possible only if a
         * compiler happens to reorder a HashEntry initialization with
         * its table assignment, which is legal under memory model
         * but is not known to ever occur.
         *
         * @param e Entry that needs to be read.
         * @return Value of entry.
         */
        V readValueUnderLock(HashEntry<K, V> e) {
            readLock().lock();

            try {
                return e.val;
            }
            finally {
                readLock().unlock();
            }
        }

        /* Specialized implementations of map methods */

        /**
         * Performs lock-free read of value for given key.
         *
         * @param key Key to be read.
         * @param hash Hash of the key
         * @return Stored value
         */
        V get(Object key, int hash) {
            if (cnt != 0) { // read-volatile
                HashEntry<K, V> e = getFirst(hash);

                while (e != null) {
                    if (e.hash == hash && key.equals(e.key)) {
                        V v = e.val;

                        if (v != null)
                            return v;

                        v = readValueUnderLock(e);

                        return v; // recheck
                    }

                    e = e.next;
                }
            }

            return null;
        }

        /**
         * Performs lock-based read of value for given key.
         * In contrast with {@link #get(Object, int)} it is guaranteed
         * to be consistent with order-based iterators.
         *
         * @param key Key to be read.
         * @param hash Hash of the key
         * @return Stored value
         */
        V getSafe(Object key, int hash) {
            readLock().lock();

            try {
                HashEntry<K, V> e = getFirst(hash);

                while (e != null) {
                    if (e.hash == hash && key.equals(e.key))
                        return e.val;

                    e = e.next;
                }

                return null;
            }
            finally {
                readLock().unlock();
            }
        }

        /**
         * Performs lock-free check of key presence.
         *
         * @param key Key to lookup.
         * @param hash Hash of the key.
         * @return {@code true} if segment contains this key.
         */
        boolean containsKey(Object key, int hash) {
            if (cnt != 0) { // read-volatile
                HashEntry<K, V> e = getFirst(hash);

                while (e != null) {
                    if (e.hash == hash && key.equals(e.key))
                        return true;

                    e = e.next;
                }
            }

            return false;
        }

        /**
         * Performs lock-free check of value presence.
         *
         * @param val Value.
         * @return {@code true} if segment contains this key.
         */
        @SuppressWarnings("ForLoopReplaceableByForEach")
        boolean containsValue(Object val) {
            if (cnt != 0) { // read-volatile
                HashEntry<K, V>[] tab = tbl;

                int len = tab.length;

                for (int i = 0; i < len; i++) {
                    for (HashEntry<K, V> e = tab[i]; e != null; e = e.next) {
                        V v = e.val;

                        if (v == null) // recheck
                            v = readValueUnderLock(e);

                        if (val.equals(v))
                            return true;
                    }
                }
            }

            return false;
        }

        /**
         * Performs value replacement for a given key with old value check.
         *
         * @param key Key to replace.
         * @param hash Hash of the key.
         * @param oldVal Old value.
         * @param newVal New value
         * @return {@code true} If value was replaced.
         */
        boolean replace(K key, int hash, V oldVal, V newVal) {
            writeLock().lock();

            boolean replaced = false;

            try {
                HashEntry<K, V> e = getFirst(hash);

                while (e != null && (e.hash != hash || !key.equals(e.key)))
                    e = e.next;

                if (e != null && oldVal.equals(e.val)) {
                    replaced = true;

                    e.val = newVal;
                }
            }
            finally {
                writeLock().unlock();
            }

            return replaced;
        }

        /**
         * Performs value replacement for a given key with old value check.
         *
         * @param key Key to replace.
         * @param hash Hash of the key.
         * @param oldVal Old value.
         * @param newVal New value
         * @return {@code oldVal}, if value was replaced, non-null object if map
         *         contained some other value and {@code null} if there were no such key.
         */
        V replacex(K key, int hash, V oldVal, V newVal) {
            writeLock().lock();

            V replaced = null;

            try {
                HashEntry<K, V> e = getFirst(hash);

                while (e != null && (e.hash != hash || !key.equals(e.key)))
                    e = e.next;

                if (e != null) {
                    if (oldVal.equals(e.val)) {
                        replaced = oldVal;

                        e.val = newVal;
                    }
                    else
                        replaced = e.val;
                }
            }
            finally {
                writeLock().unlock();
            }

            return replaced;
        }

        V replace(K key, int hash, V newVal) {
            writeLock().lock();

            V oldVal = null;

            try {
                HashEntry<K, V> e = getFirst(hash);

                while (e != null && (e.hash != hash || !key.equals(e.key)))
                    e = e.next;

                if (e != null) {
                    oldVal = e.val;

                    e.val = newVal;
                }
            }
            finally {
                writeLock().unlock();
            }

            return oldVal;
        }

        V put(K key, int hash, V val, boolean onlyIfAbsent) {
            writeLock().lock();

            V oldVal;

            boolean added = false;

            try {
                int c = cnt;

                if (c++ > threshold) // ensure capacity
                    rehash();

                HashEntry<K, V>[] tab = tbl;

                int idx = hash & (tab.length - 1);

                HashEntry<K, V> first = tab[idx];

                HashEntry<K, V> e = first;

                while (e != null && (e.hash != hash || !key.equals(e.key)))
                    e = e.next;

                boolean modified = false;

                if (e != null) {
                    oldVal = e.val;

                    if (!onlyIfAbsent) {
                        e.val = val;

                        ConcurrentLinkedDeque8.Node node = e.node;

                        if (node != null) {
                            HashEntry<K, V> qEntry = (HashEntry<K, V>)node.item();

                            if (qEntry != null && qEntry != e)
                                qEntry.val = val;
                        }

                        modified = true;
                    }
                }
                else {
                    oldVal = null;

                    ++modCnt;

                    size.increment();

                    e = tab[idx] = new HashEntry<>(key, hash, first, val);

                    ConcurrentLinkedHashMap.this.modCnt.increment();

                    e.modCnt = ConcurrentLinkedHashMap.this.modCnt.intValue();

                    cnt = c; // write-volatile

                    added = true;
                }

                assert !(added && modified);

                if (added) {
                    switch (qPlc) {
                        case PER_SEGMENT_Q_OPTIMIZED_RMV:
                            recordInsert(e, (ConcurrentLinkedDeque8)segEntryQ);

                            if (maxCap > 0)
                                checkRemoveEldestEntrySegment(c);

                            break;

                        case PER_SEGMENT_Q:
                            segEntryQ.add(e);

                            if (maxCap > 0)
                                checkRemoveEldestEntrySegment(c);

                            break;

                        default:
                            assert qPlc == SINGLE_Q;

                            recordInsert(e, entryQ);
                    }
                }
            }
            finally {
                writeLock().unlock();
            }

            if (qPlc == SINGLE_Q && added && maxCap > 0)
                checkRemoveEldestEntry();

            return oldVal;
        }

        /**
         * @param cnt Segment entries count.
         */
        private void checkRemoveEldestEntrySegment(int cnt) {
            assert maxCap > 0;

            if (cnt - ((maxCap / segments.length) + 1) > 0) {
                HashEntry<K, V> e0 = segEntryQ.poll();

                assert e0 != null;

                removeLocked(
                    e0.key,
                    e0.hash,
                    null /*no need to compare*/,
                    false);
            }
        }

        /**
         * This method is called under the segment lock.
         */
        @SuppressWarnings({"ForLoopReplaceableByForEach"})
        void rehash() {
            HashEntry<K, V>[] oldTbl = tbl;
            int oldCap = oldTbl.length;

            if (oldCap >= MAX_CAP_LIMIT)
                return;

            /*
             * Reclassify nodes in each list to new Map.  Because we are
             * using power-of-two expansion, the elements from each bin
             * must either stay at same index, or move with a power of two
             * offset. We eliminate unnecessary node creation by catching
             * cases where old nodes can be reused because their next
             * fields won't change. Statistically, at the default
             * threshold, only about one-sixth of them need cloning when
             * a table doubles. The nodes they replace will be garbage
             * collectable as soon as they are no longer referenced by any
             * reader thread that may be in the midst of traversing table
             * right now.
             */

            int c = cnt;

            HashEntry<K, V>[] newTbl = HashEntry.newArray(oldCap << 1);

            threshold = (int)(newTbl.length * loadFactor);

            int sizeMask = newTbl.length - 1;

            for (int i = 0; i < oldCap; i++) {
                // We need to guarantee that any existing reads of old Map can
                //  proceed. So we cannot yet null out each bin.
                HashEntry<K, V> e = oldTbl[i];

                if (e != null) {
                    HashEntry<K, V> next = e.next;

                    int idx = e.hash & sizeMask;

                    //  Single node on list
                    if (next == null)
                        newTbl[idx] = e;

                    else {
                        // Reuse trailing consecutive sequence at same slot
                        HashEntry<K, V> lastRun = e;

                        int lastIdx = idx;

                        for (HashEntry<K, V> last = next; last != null; last = last.next) {
                            int k = last.hash & sizeMask;

                            if (k != lastIdx) {
                                lastIdx = k;
                                lastRun = last;
                            }
                        }

                        newTbl[lastIdx] = lastRun;

                        // Clone all remaining nodes
                        for (HashEntry<K, V> p = e; p != lastRun; p = p.next) {
                            int k = p.hash & sizeMask;

                            HashEntry<K, V> n = newTbl[k];

                            HashEntry<K, V> e0 = new HashEntry<>(p.key, p.hash, n, p.val, p.node, p.modCnt);

                            newTbl[k] = e0;
                        }
                    }
                }
            }

            cnt = c;

            tbl = newTbl;
        }

        /**
         * Remove; match on key only if value null, else match both.
         *
         * @param key Key to be removed.
         * @param hash Hash of the key.
         * @param val Value to match.
         * @param cleanupQ {@code True} if need to cleanup queue.
         * @return Old value, if entry existed, {@code null} otherwise.
         */
        V remove(Object key, int hash, Object val, boolean cleanupQ) {
            writeLock().lock();

            try {
                return removeLocked(key, hash, val, cleanupQ);
            }
            finally {
                writeLock().unlock();
            }
        }

        /**
         * Locked version of remove. Match on key only if value null, else match both.
         *
         * @param key Key to be removed.
         * @param hash Hash of the key.
         * @param val Value to match.
         * @param cleanupQ {@code True} if need to cleanup queue.
         * @return Old value, if entry existed, {@code null} otherwise.
         */
        @SuppressWarnings({"unchecked"})
        V removeLocked(Object key, int hash, Object val, boolean cleanupQ) {
            int c = cnt - 1;

            HashEntry<K, V>[] tab = tbl;

            int idx = hash & (tab.length - 1);

            HashEntry<K, V> first = tab[idx];

            HashEntry<K, V> e = first;

            while (e != null && (e.hash != hash || !key.equals(e.key)))
                e = e.next;

            V oldVal = null;

            if (e != null) {
                V v = e.val;

                if (val == null || val.equals(v)) {
                    oldVal = v;

                    // All entries following removed node can stay
                    // in list, but all preceding ones need to be
                    // cloned.
                    ++modCnt;

                    ConcurrentLinkedHashMap.this.modCnt.increment();

                    HashEntry<K, V> newFirst = e.next;

                    for (HashEntry<K, V> p = first; p != e; p = p.next)
                        newFirst = new HashEntry<>(p.key, p.hash, newFirst, p.val, p.node, p.modCnt);

                    tab[idx] = newFirst;

                    cnt = c; // write-volatile

                    size.decrement();
                }
            }

            if (oldVal != null && cleanupQ) {
                switch (qPlc) {
                    case PER_SEGMENT_Q_OPTIMIZED_RMV:
                        ((ConcurrentLinkedDeque8)segEntryQ).unlinkx(e.node);

                        e.node = null;

                        break;

                    case PER_SEGMENT_Q:
                        // Linear time method call.
                        segEntryQ.remove(e);

                        break;

                    default:
                        assert qPlc == SINGLE_Q;

                        entryQ.unlinkx(e.node);

                        e.node = null;
                }
            }

            return oldVal;
        }
    }

    /* ---------------- Public operations -------------- */

    /**
     * Creates a new, empty map with the specified initial
     * capacity, load factor, concurrency level and max capacity.
     *
     * @param initCap the initial capacity. The implementation
     *      performs internal sizing to accommodate this many elements.
     * @param loadFactor  the load factor threshold, used to control resizing.
     *      Resizing may be performed when the average number of elements per
     *      bin exceeds this threshold.
     * @param concurLvl the estimated number of concurrently
     *      updating threads. The implementation performs internal sizing
     *      to try to accommodate this many threads.
     * @param maxCap Max capacity ({@code 0} for unbounded).
     * @param qPlc Queue policy.
     * @throws IllegalArgumentException if the initial capacity is
     *      negative or the load factor or concurLvl are
     *      non-positive.
     */
    @SuppressWarnings({"unchecked"})
    public ConcurrentLinkedHashMap(int initCap, float loadFactor, int concurLvl, int maxCap, QueuePolicy qPlc) {
        if (!(loadFactor > 0) || initCap < 0 || concurLvl <= 0)
            throw new IllegalArgumentException();

        if (concurLvl > MAX_SEGS)
            concurLvl = MAX_SEGS;

        this.maxCap = maxCap;
        this.qPlc = qPlc;

        entryQ = qPlc == SINGLE_Q ? new ConcurrentLinkedDeque8<HashEntry<K, V>>() : null;

        // Find power-of-two sizes best matching arguments
        int sshift = 0;

        int ssize = 1;

        while (ssize < concurLvl) {
            ++sshift;
            ssize <<= 1;
        }

        segmentShift = 32 - sshift;

        segmentMask = ssize - 1;

        segments = new Segment[ssize];

        if (initCap > MAX_CAP_LIMIT)
            initCap = MAX_CAP_LIMIT;

        int c = initCap / ssize;

        if (c * ssize < initCap)
            ++c;

        int cap = 1;

        while (cap < c)
            cap <<= 1;

        for (int i = 0; i < segments.length; ++i)
            segments[i] = new Segment<>(cap, loadFactor);
    }

    /**
     * Creates a new, empty map with the specified initial
     * capacity, load factor, concurrency level and max capacity.
     *
     * @param initCap the initial capacity. The implementation
     *      performs internal sizing to accommodate this many elements.
     * @param loadFactor  the load factor threshold, used to control resizing.
     *      Resizing may be performed when the average number of elements per
     *      bin exceeds this threshold.
     * @param concurLvl the estimated number of concurrently
     *      updating threads. The implementation performs internal sizing
     *      to try to accommodate this many threads.
     * @param maxCap Max capacity ({@code 0} for unbounded).
     * @throws IllegalArgumentException if the initial capacity is
     *      negative or the load factor or concurLvl are
     *      non-positive.
     */
    public ConcurrentLinkedHashMap(int initCap, float loadFactor, int concurLvl, int maxCap) {
        this(initCap, loadFactor, concurLvl, maxCap, SINGLE_Q);
    }

    /**
     * Creates a new, empty map with the specified initial
     * capacity, load factor and concurrency level.
     *
     * @param initCap the initial capacity. The implementation
     *      performs internal sizing to accommodate this many elements.
     * @param loadFactor  the load factor threshold, used to control resizing.
     *      Resizing may be performed when the average number of elements per
     *      bin exceeds this threshold.
     * @param concurLvl the estimated number of concurrently
     *      updating threads. The implementation performs internal sizing
     *      to try to accommodate this many threads.
     * @throws IllegalArgumentException if the initial capacity is
     *      negative or the load factor or concurLvl are
     *      non-positive.
     */
    public ConcurrentLinkedHashMap(int initCap, float loadFactor, int concurLvl) {
        this(initCap, loadFactor, concurLvl, 0);
    }

    /**
     * Creates a new, empty map with the specified initial capacity
     * and load factor and with the default concurrencyLevel (16).
     *
     * @param initCap The implementation performs internal
     * sizing to accommodate this many elements.
     * @param loadFactor  the load factor threshold, used to control resizing.
     * Resizing may be performed when the average number of elements per
     * bin exceeds this threshold.
     * @throws IllegalArgumentException if the initial capacity of
     * elements is negative or the load factor is non-positive
     *
     * @since 1.6
     */
    public ConcurrentLinkedHashMap(int initCap, float loadFactor) {
        this(initCap, loadFactor, DFLT_CONCUR_LVL);
    }

    /**
     * Creates a new, empty map with the specified initial capacity,
     * and with default load factor (0.75) and concurrencyLevel (16).
     *
     * @param initCap the initial capacity. The implementation
     * performs internal sizing to accommodate this many elements.
     * @throws IllegalArgumentException if the initial capacity of
     * elements is negative.
     */
    public ConcurrentLinkedHashMap(int initCap) {
        this(initCap, DFLT_LOAD_FACTOR, DFLT_CONCUR_LVL);
    }

    /**
     * Creates a new, empty map with a default initial capacity (16),
     * load factor (0.75) and concurrencyLevel (16).
     */
    public ConcurrentLinkedHashMap() {
        this(DFLT_INIT_CAP, DFLT_LOAD_FACTOR, DFLT_CONCUR_LVL);
    }

    /**
     * Creates a new map with the same mappings as the given map.
     * The map is created with a capacity of 1.5 times the number
     * of mappings in the given map or 16 (whichever is greater),
     * and a default load factor (0.75) and concurrencyLevel (16).
     *
     * @param m the map
     */
    public ConcurrentLinkedHashMap(Map<? extends K, ? extends V> m) {
        this(Math.max((int) (m.size() / DFLT_LOAD_FACTOR) + 1, DFLT_INIT_CAP),
            DFLT_LOAD_FACTOR, DFLT_CONCUR_LVL);

        putAll(m);
    }

    /**
     * Returns <tt>true</tt> if this map contains no key-value mappings.
     *
     * @return <tt>true</tt> if this map contains no key-value mappings.
     */
    @Override public boolean isEmpty() {
        Segment<K, V>[] segments = this.segments;
        /*
         * We keep track of per-segment modCounts to avoid ABA
         * problems in which an element in one segment was added and
         * in another removed during traversal, in which case the
         * table was never actually empty at any point. Note the
         * similar use of modCounts in the size() and containsValue()
         * methods, which are the only other methods also susceptible
         * to ABA problems.
         */
        int[] mc = new int[segments.length];
        int mcsum = 0;

        for (int i = 0; i < segments.length; ++i) {
            if (segments[i].cnt != 0)
                return false;
            else
                mcsum += mc[i] = segments[i].modCnt;
        }

        // If mcsum happens to be zero, then we know we got a snapshot
        // before any modifications at all were made.  This is
        // probably common enough to bother tracking.
        if (mcsum != 0) {
            for (int i = 0; i < segments.length; ++i) {
                if (segments[i].cnt != 0 ||
                    mc[i] != segments[i].modCnt)
                    return false;
            }
        }

        return true;
    }

    /**
     * Returns the number of key-value mappings in this map.  If the
     * map contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
     * <tt>Integer.MAX_VALUE</tt>.
     *
     * @return the number of key-value mappings in this map
     */
    @SuppressWarnings({"LockAcquiredButNotSafelyReleased"})
    @Override public int size() {
        Segment<K, V>[] segments = this.segments;
        long sum = 0;
        long check = 0;
        int[] mc = new int[segments.length];

        // Try a few times to get accurate count. On failure due to
        // continuous async changes in table, resort to locking.
        for (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) {
            check = 0;
            sum = 0;
            int mcsum = 0;

            for (int i = 0; i < segments.length; ++i) {
                sum += segments[i].cnt;
                mcsum += mc[i] = segments[i].modCnt;
            }

            if (mcsum != 0) {
                for (int i = 0; i < segments.length; ++i) {
                    check += segments[i].cnt;

                    if (mc[i] != segments[i].modCnt) {
                        check = -1; // force retry

                        break;
                    }
                }
            }

            if (check == sum)
                break;
        }

        if (check != sum) { // Resort to locking all segments
            sum = 0;

            for (Segment<K, V> segment : segments)
                segment.readLock().lock();

            for (Segment<K, V> segment : segments)
                sum += segment.cnt;

            for (Segment<K, V> segment : segments)
                segment.readLock().unlock();
        }

        return sum > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)sum;
    }

    /**
     * @return The number of key-value mappings in this map (constant-time).
     */
    public int sizex() {
        int i = size.intValue();

        return i > 0 ? i : 0;
    }

    /**
     * @return <tt>true</tt> if this map contains no key-value mappings
     */
    public boolean isEmptyx() {
        return sizex() == 0;
    }

    /**
     * Returns the value to which the specified key is mapped,
     * or {@code null} if this map contains no mapping for the key.
     *
     * <p>More formally, if this map contains a mapping from a key
     * {@code k} to a value {@code v} such that {@code key.equals(k)},
     * then this method returns {@code v}; otherwise it returns
     * {@code null}.  (There can be at most one such mapping.)
     *
     * @throws NullPointerException if the specified key is null
     */
    @Override public V get(Object key) {
        int hash = hash(key.hashCode());

        return segmentFor(hash).get(key, hash);
    }

    /**
     * Returns the value to which the specified key is mapped,
     * or {@code null} if this map contains no mapping for the key.
     *
     * <p>More formally, if this map contains a mapping from a key
     * {@code k} to a value {@code v} such that {@code key.equals(k)},
     * then this method returns {@code v}; otherwise it returns
     * {@code null}.  (There can be at most one such mapping.)
     *
     * In contrast with {@link #get(Object)} this method acquires
     * read lock on segment where the key is mapped.
     *
     * @throws NullPointerException if the specified key is null
     */
    public V getSafe(Object key) {
        int hash = hash(key.hashCode());

        return segmentFor(hash).getSafe(key, hash);
    }

    /**
     * Tests if the specified object is a key in this table.
     *
     * @param  key   possible key
     * @return <tt>true</tt> if and only if the specified object
     *         is a key in this table, as determined by the
     *         <tt>equals</tt> method; <tt>false</tt> otherwise.
     * @throws NullPointerException if the specified key is null
     */
    @Override public boolean containsKey(Object key) {
        int hash = hash(key.hashCode());

        return segmentFor(hash).containsKey(key, hash);
    }

    /**
     * Returns <tt>true</tt> if this map maps one or more keys to the
     * specified value. Note: This method requires a full internal
     * traversal of the hash table, and so is much slower than
     * method <tt>containsKey</tt>.
     *
     * @param val value whose presence in this map is to be tested
     * @return <tt>true</tt> if this map maps one or more keys to the
     *         specified value
     * @throws NullPointerException if the specified value is null
     */
    @SuppressWarnings({"LockAcquiredButNotSafelyReleased"})
    @Override public boolean containsValue(Object val) {
        if (val == null)
            throw new NullPointerException();

        // See explanation of modCount use above

        Segment<K, V>[] segments = this.segments;
        int[] mc = new int[segments.length];

        // Try a few times without locking
        for (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) {
            int mcsum = 0;

            for (int i = 0; i < segments.length; ++i) {
                mcsum += mc[i] = segments[i].modCnt;

                if (segments[i].containsValue(val))
                    return true;
            }

            boolean cleanSweep = true;

            if (mcsum != 0) {
                for (int i = 0; i < segments.length; ++i) {
                    if (mc[i] != segments[i].modCnt) {
                        cleanSweep = false;

                        break;
                    }
                }
            }

            if (cleanSweep)
                return false;
        }

        // Resort to locking all segments
        for (Segment<K, V> segment : segments)
            segment.readLock().lock();

        boolean found = false;

        try {
            for (Segment<K, V> segment : segments) {
                if (segment.containsValue(val)) {
                    found = true;

                    break;
                }
            }
        } finally {
            for (Segment<K, V> segment : segments)
                segment.readLock().unlock();
        }

        return found;
    }

    /**
     * Legacy method testing if some key maps into the specified value
     * in this table.  This method is identical in functionality to
     * {@link #containsValue}, and exists solely to ensure
     * full compatibility with class {@link java.util.Hashtable},
     * which supported this method prior to introduction of the
     * Java Collections framework.

     * @param  val a value to search for
     * @return <tt>true</tt> if and only if some key maps to the
     *         <tt>value</tt> argument in this table as
     *         determined by the <tt>equals</tt> method;
     *         <tt>false</tt> otherwise
     * @throws NullPointerException if the specified value is null
     */
    public boolean contains(Object val) {
        return containsValue(val);
    }

    /**
     * Maps the specified key to the specified value in this table.
     * Neither the key nor the value can be null.
     *
     * <p> The value can be retrieved by calling the <tt>get</tt> method
     * with a key that is equal to the original key.
     *
     * @param key key with which the specified value is to be associated
     * @param val value to be associated with the specified key
     * @return the previous value associated with <tt>key</tt>, or
     *         <tt>null</tt> if there was no mapping for <tt>key</tt>
     * @throws NullPointerException if the specified key or value is null
     */
    @Override public V put(K key, V val) {
        if (val == null)
            throw new NullPointerException();

        int hash = hash(key.hashCode());

        return segmentFor(hash).put(key, hash, val, false);
    }

    /**
     * {@inheritDoc}
     *
     * @return the previous value associated with the specified key,
     *         or <tt>null</tt> if there was no mapping for the key
     * @throws NullPointerException if the specified key or value is null
     */
    @Override public V putIfAbsent(K key, V val) {
        if (val == null)
            throw new NullPointerException();

        int hash = hash(key.hashCode());

        return segmentFor(hash).put(key, hash, val, true);
    }

    /**
     * Copies all of the mappings from the specified map to this one.
     * These mappings replace any mappings that this map had for any of the
     * keys currently in the specified map.
     *
     * @param m mappings to be stored in this map
     */
    @Override public void putAll(Map<? extends K, ? extends V> m) {
        for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
            put(e.getKey(), e.getValue());
    }

    /**
     * Removes the key (and its corresponding value) from this map.
     * This method does nothing if the key is not in the map.
     *
     * @param  key the key that needs to be removed
     * @return the previous value associated with <tt>key</tt>, or
     *         <tt>null</tt> if there was no mapping for <tt>key</tt>
     * @throws NullPointerException if the specified key is null
     */
    @Override public V remove(Object key) {
        int hash = hash(key.hashCode());

        return segmentFor(hash).remove(key, hash, null, true);
    }

    /**
     * {@inheritDoc}
     *
     * @throws NullPointerException if the specified key is null
     */
    @Override public boolean remove(Object key, Object val) {
        int hash = hash(key.hashCode());

        return val != null && segmentFor(hash).remove(key, hash, val, true) != null;
    }

    /**
     * {@inheritDoc}
     *
     * @throws NullPointerException if any of the arguments are null
     */
    @Override public boolean replace(K key, V oldVal, V newVal) {
        if (oldVal == null || newVal == null)
            throw new NullPointerException();

        int hash = hash(key.hashCode());

        return segmentFor(hash).replace(key, hash, oldVal, newVal);
    }

    /**
     * Replaces the entry for a key only if currently mapped to a given value.
     * This is equivalent to
     * <pre>
     *   if (map.containsKey(key)) {
     *       if (map.get(key).equals(oldValue)) {
     *           map.put(key, newValue);
     *           return oldValue;
     *      } else
     *          return map.get(key);
     *   } else return null;</pre>
     * except that the action is performed atomically.
     *
     * @param key key with which the specified value is associated
     * @param oldVal value expected to be associated with the specified key
     * @param newVal value to be associated with the specified key
     * @return {@code oldVal}, if value was replaced, non-null previous value if map
     *        contained some other value and {@code null} if there were no such key.
     */
    public V replacex(K key, V oldVal, V newVal) {
        if (oldVal == null || newVal == null)
            throw new NullPointerException();

        int hash = hash(key.hashCode());

        return segmentFor(hash).replacex(key, hash, oldVal, newVal);
    }

    /**
     * {@inheritDoc}
     *
     * @return the previous value associated with the specified key,
     *         or <tt>null</tt> if there was no mapping for the key
     * @throws NullPointerException if the specified key or value is null
     */
    @Override public V replace(K key, V val) {
        if (val == null)
            throw new NullPointerException();

        int hash = hash(key.hashCode());

        return segmentFor(hash).replace(key, hash, val);
    }

    /**
     * Removes all of the mappings from this map.
     */
    @Override public void clear() {
        throw new UnsupportedOperationException();
    }

    /**
     * Returns a {@link Set} view of the keys contained in this map.
     * The set is backed by the map, so changes to the map are
     * reflected in the set, and vice-versa.  The set supports element
     * removal, which removes the corresponding mapping from this map,
     * via the <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
     * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
     * operations.  It does not support the <tt>add</tt> or
     * <tt>addAll</tt> operations.
     *
     * <p>The view's <tt>iterator</tt> is a "weakly consistent" iterator
     * that will never throw {@link ConcurrentModificationException},
     * and guarantees to traverse elements as they existed upon
     * construction of the iterator, and may (but is not guaranteed to)
     * reflect any modifications subsequent to construction.
     */
    @Override public Set<K> keySet() {
        Set<K> ks = keySet;

        return (ks != null) ? ks : (keySet = new KeySet());
    }

    /**
     * Returns a {@link Set} view of the keys contained in this map
     * in descending order.
     * The set is backed by the map, so changes to the map are
     * reflected in the set, and vice-versa.  The set supports element
     * removal, which removes the corresponding mapping from this map,
     * via the <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
     * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
     * operations.  It does not support the <tt>add</tt> or
     * <tt>addAll</tt> operations.
     *
     * <p>The view's <tt>iterator</tt> is a "weakly consistent" iterator
     * that will never throw {@link ConcurrentModificationException},
     * and guarantees to traverse elements as they existed upon
     * construction of the iterator, and may (but is not guaranteed to)
     * reflect any modifications subsequent to construction.
     */
    public Set<K> descendingKeySet() {
        Set<K> ks = descKeySet;

        return (ks != null) ? ks : (descKeySet = new KeySetDescending());
    }

    /**
     * Returns a {@link Collection} view of the values contained in this map.
     * The collection is backed by the map, so changes to the map are
     * reflected in the collection, and vice-versa.  The collection
     * supports element removal, which removes the corresponding
     * mapping from this map, via the <tt>Iterator.remove</tt>,
     * <tt>Collection.remove</tt>, <tt>removeAll</tt>,
     * <tt>retainAll</tt>, and <tt>clear</tt> operations.  It does not
     * support the <tt>add</tt> or <tt>addAll</tt> operations.
     *
     * <p>The view's <tt>iterator</tt> is a "weakly consistent" iterator
     * that will never throw {@link ConcurrentModificationException},
     * and guarantees to traverse elements as they existed upon
     * construction of the iterator, and may (but is not guaranteed to)
     * reflect any modifications subsequent to construction.
     */
    @Override public Collection<V> values() {
        Collection<V> vs = vals;

        return (vs != null) ? vs : (vals = new Values());
    }

    /**
     * Returns a {@link Collection} view of the values contained in this map
     * in descending order.
     * The collection is backed by the map, so changes to the map are
     * reflected in the collection, and vice-versa.  The collection
     * supports element removal, which removes the corresponding
     * mapping from this map, via the <tt>Iterator.remove</tt>,
     * <tt>Collection.remove</tt>, <tt>removeAll</tt>,
     * <tt>retainAll</tt>, and <tt>clear</tt> operations.  It does not
     * support the <tt>add</tt> or <tt>addAll</tt> operations.
     *
     * <p>The view's <tt>iterator</tt> is a "weakly consistent" iterator
     * that will never throw {@link ConcurrentModificationException},
     * and guarantees to traverse elements as they existed upon
     * construction of the iterator, and may (but is not guaranteed to)
     * reflect any modifications subsequent to construction.
     */
    public Collection<V> descendingValues() {
        Collection<V> vs = descVals;

        return (vs != null) ? vs : (descVals = new ValuesDescending());
    }

    /**
     * Returns a {@link Set} view of the mappings contained in this map.
     * The set is backed by the map, so changes to the map are
     * reflected in the set, and vice-versa.  The set supports element
     * removal, which removes the corresponding mapping from the map,
     * via the <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
     * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
     * operations.  It does not support the <tt>add</tt> or
     * <tt>addAll</tt> operations.
     *
     * <p>The view's <tt>iterator</tt> is a "weakly consistent" iterator
     * that will never throw {@link ConcurrentModificationException},
     * and guarantees to traverse elements as they existed upon
     * construction of the iterator, and may (but is not guaranteed to)
     * reflect any modifications subsequent to construction.
     */
    @Override public Set<Map.Entry<K, V>> entrySet() {
        Set<Map.Entry<K, V>> es = entrySet;

        return (es != null) ? es : (entrySet = new EntrySet());
    }

    /**
     * Returns a {@link Set} view of the mappings contained in this map
     * in descending order.
     * The set is backed by the map, so changes to the map are
     * reflected in the set, and vice-versa.  The set supports element
     * removal, which removes the corresponding mapping from the map,
     * via the <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
     * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
     * operations.  It does not support the <tt>add</tt> or
     * <tt>addAll</tt> operations.
     *
     * <p>The view's <tt>iterator</tt> is a "weakly consistent" iterator
     * that will never throw {@link ConcurrentModificationException},
     * and guarantees to traverse elements as they existed upon
     * construction of the iterator, and may (but is not guaranteed to)
     * reflect any modifications subsequent to construction.
     */
    public Set<Map.Entry<K, V>> descendingEntrySet() {
        Set<Map.Entry<K, V>> es = descEntrySet;

        return (es != null) ? es : (descEntrySet = new EntrySetDescending());
    }

    /**
     * Returns an enumeration of the keys in this table.
     *
     * @return an enumeration of the keys in this table.
     * @see #keySet()
     */
    public Enumeration<K> keys() {
        return new KeyIterator(true);
    }

    /**
     * Returns an enumeration of the keys in this table in descending order.
     *
     * @return an enumeration of the keys in this table in descending order.
     * @see #keySet()
     */
    public Enumeration<K> descendingKeys() {
        return new KeyIterator(false);
    }

    /**
     * Returns an enumeration of the values in this table.
     *
     * @return an enumeration of the values in this table.
     * @see #values()
     */
    public Enumeration<V> elements() {
        return new ValueIterator(true);
    }

    /**
     * Returns an enumeration of the values in this table in descending order.
     *
     * @return an enumeration of the values in this table in descending order.
     * @see #values()
     */
    public Enumeration<V> descendingElements() {
        return new ValueIterator(false);
    }

    /**
     * This method is called by hash map whenever a new entry is inserted into map.
     * <p>
     * This method is called outside the segment-protection lock and may be called concurrently.
     *
     * @param e The new inserted entry.
     */
    @SuppressWarnings({"unchecked"})
    private void recordInsert(HashEntry e, ConcurrentLinkedDeque8 q) {
        e.node = q.addx(e);
    }

    /**
     * Concurrently removes eldest entry from the map.
     */
    private void checkRemoveEldestEntry() {
        assert maxCap > 0;
        assert qPlc == SINGLE_Q;

        int sizex = sizex();

        for (int i = maxCap; i < sizex; i++) {
            HashEntry<K, V> e = entryQ.poll();

            if (e != null)
                segmentFor(e.hash).remove(e.key, e.hash, e.val, false);
            else
                return;

            if (sizex() <= maxCap)
                return;
        }
    }

    /**
     * This method is intended for test purposes only.
     *
     * @return Queue.
     */
    public ConcurrentLinkedDeque8<HashEntry<K, V>> queue() {
        return entryQ;
    }

    /**
     * @return Queue policy.
     */
    public QueuePolicy policy() {
        return qPlc;
    }

    /**
     * Class implementing iteration over map entries.
     */
    private abstract class HashIterator {
        /** Underlying collection iterator. */
        private Iterator<HashEntry<K, V>> delegate;

        /** Last returned entry, used in {@link #remove()} method. */
        private HashEntry<K, V> lastReturned;

        /** Next entry to return */
        private HashEntry<K, V> nextEntry;

        /** The map modification count at the creation time. */
        private int modCnt;

        /**
         * @param asc {@code True} for ascending iterator.
         */
        HashIterator(boolean asc) {
            modCnt = ConcurrentLinkedHashMap.this.modCnt.intValue();

            // Init delegate.
            switch (qPlc) {
                case SINGLE_Q:
                    delegate = asc ? entryQ.iterator() : entryQ.descendingIterator();

                    break;

                default:
                    assert qPlc == PER_SEGMENT_Q || qPlc == PER_SEGMENT_Q_OPTIMIZED_RMV : qPlc;

                    delegate = new HashIteratorDelegate();
            }

            advance();
        }

        /**
         * @return {@code true} If iterator has elements to iterate.
         */
        public boolean hasMoreElements() {
            return hasNext();
        }

        /**
         * @return {@code true} If iterator has elements to iterate.
         */
        public boolean hasNext() {
            return nextEntry != null;
        }

        /**
         * @return Next entry.
         */
        HashEntry<K, V> nextEntry() {
            if (nextEntry == null)
                throw new NoSuchElementException();

            lastReturned = nextEntry;

            advance();

            return lastReturned;
        }

        /**
         * Removes entry returned by {@link #nextEntry()}.
         */
        public void remove() {
            if (lastReturned == null)
                throw new IllegalStateException();

            ConcurrentLinkedHashMap.this.remove(lastReturned.key);

            lastReturned = null;
        }

        /**
         * Moves iterator to the next position.
         */
        private void advance() {
            nextEntry = null;

            while (delegate.hasNext()) {
                HashEntry<K, V> n = delegate.next();

                if (n.modCnt <= modCnt) {
                    nextEntry = n;

                    break;
                }
            }
        }
    }

    /**
     *
     */
    private class HashIteratorDelegate implements Iterator<HashEntry<K, V>> {
        /** */
        private HashEntry<K, V>[] curTbl;

        /** */
        private int nextSegIdx;

        /** */
        private int nextTblIdx;

        /** */
        private HashEntry<K, V> next;

        /** */
        private HashEntry<K, V> next0;

        /** */
        private HashEntry<K, V> cur;

        /**
         *
         */
        public HashIteratorDelegate() {
            nextSegIdx = segments.length - 1;
            nextTblIdx = -1;

            advance();
        }

        /**
         *
         */
        private void advance() {
            if (next0 != null && advanceInBucket(next0, true))
                return;

            while (nextTblIdx >= 0) {
                HashEntry<K, V> bucket = curTbl[nextTblIdx--];

                if (bucket != null && advanceInBucket(bucket, false))
                    return;
            }

            while (nextSegIdx >= 0) {
                int nextSegIdx0 = nextSegIdx--;

                Segment seg = segments[nextSegIdx0];

                curTbl = seg.tbl;

                for (int j = curTbl.length - 1; j >= 0; --j) {
                    HashEntry<K, V> bucket = curTbl[j];

                    if (bucket != null && advanceInBucket(bucket, false)) {
                        nextTblIdx = j - 1;

                        return;
                    }
                }
            }
        }

        /**
         * @param e Current next.
         * @return {@code True} if advance succeeded.
         */
        private boolean advanceInBucket(@Nullable HashEntry<K, V> e, boolean skipFirst) {
            if (e == null)
                return false;

            next0 = e;

            do {
                if (!skipFirst) {
                    next = next0;

                    return true;
                }
                else
                    skipFirst = false;
            }
            while ((next0 = next0.next) != null);

            assert next0 == null;

            next = null;

            return false;
        }

        /** {@inheritDoc} */
        @Override public boolean hasNext() {
            return next != null;
        }

        /** {@inheritDoc} */
        @Override public HashEntry<K, V> next() {
            HashEntry<K, V> e = next;

            if (e == null)
                throw new NoSuchElementException();

            advance();

            return e;
        }

        /** {@inheritDoc} */
        @Override public void remove() {
            if (cur == null)
                throw new IllegalStateException();

            HashEntry<K, V> e = cur;

            cur = null;

            ConcurrentLinkedHashMap.this.remove(e.key, e.val);
        }
    }

    /**
     * Key iterator implementation.
     */
    private final class KeyIterator extends HashIterator implements Iterator<K>, Enumeration<K> {
        /**
         * @param asc {@code True} for ascending iterator.
         */
        private KeyIterator(boolean asc) {
            super(asc);
        }

        /** {@inheritDoc} */
        @Override public K next() {
            return nextEntry().key;
        }

        /** {@inheritDoc} */
        @Override public K nextElement() {
            return nextEntry().key;
        }
    }

    /**
     * Value iterator implementation.
     */
    private final class ValueIterator extends HashIterator implements Iterator<V>, Enumeration<V> {
        /**
         * @param asc {@code True} for ascending iterator.
         */
        private ValueIterator(boolean asc) {
            super(asc);
        }

        /** {@inheritDoc} */
        @Override public V next() {
            return nextEntry().val;
        }

        /** {@inheritDoc} */
        @Override public V nextElement() {
            return nextEntry().val;
        }
    }

    /**
     * Custom Entry class used by EntryIterator.next(), that relays
     * setValue changes to the underlying map.
     */
    private final class WriteThroughEntry extends AbstractMap.SimpleEntry<K, V> {
        /**
         * @param k Key
         * @param v Value
         */
        WriteThroughEntry(K k, V v) {
            super(k, v);
        }

        /**
         * Set our entry's value and write through to the map. The
         * value to return is somewhat arbitrary here. Since a
         * WriteThroughEntry does not necessarily track asynchronous
         * changes, the most recent "previous" value could be
         * different from what we return (or could even have been
         * removed in which case the put will re-establish). We do not
         * and cannot guarantee more.
         */
        @Override public V setValue(V val) {
            if (val == null)
                throw new NullPointerException();

            V v = super.setValue(val);

            put(getKey(), val);

            return v;
        }
    }

    /**
     * Entry iterator implementation.
     */
    private final class EntryIterator extends HashIterator implements Iterator<Entry<K, V>> {
        /**
         * @param asc {@code True} for ascending iterator.
         */
        private EntryIterator(boolean asc) {
            super(asc);
        }

        /** {@inheritDoc} */
        @Override public Map.Entry<K, V> next() {
            HashEntry<K, V> e = nextEntry();

            return new WriteThroughEntry(e.key, e.val);
        }
    }

    /**
     * Key set of the map.
     */
    private abstract class AbstractKeySet extends AbstractSet<K> {
        /** {@inheritDoc} */
        @Override public int size() {
            return ConcurrentLinkedHashMap.this.size();
        }

        /** {@inheritDoc} */
        @Override public boolean contains(Object o) {
            return containsKey(o);
        }

        /** {@inheritDoc} */
        @Override public boolean remove(Object o) {
            return ConcurrentLinkedHashMap.this.remove(o) != null;
        }

        /** {@inheritDoc} */
        @Override public void clear() {
            ConcurrentLinkedHashMap.this.clear();
        }
    }

    /**
     * Key set of the map.
     */
    private final class KeySet extends AbstractKeySet {
        /** {@inheritDoc} */
        @Override public Iterator<K> iterator() {
            return new KeyIterator(true);
        }
    }

    /**
     * Key set of the map.
     */
    private final class KeySetDescending extends AbstractKeySet {
        /** {@inheritDoc} */
        @Override public Iterator<K> iterator() {
            return new KeyIterator(false);
        }
    }

    /**
     * Values collection of the map.
     */
    private abstract class AbstractValues extends AbstractCollection<V> {
        /** {@inheritDoc} */
        @Override public int size() {
            return ConcurrentLinkedHashMap.this.size();
        }

        /** {@inheritDoc} */
        @Override public boolean contains(Object o) {
            return containsValue(o);
        }

        /** {@inheritDoc} */
        @Override public void clear() {
            ConcurrentLinkedHashMap.this.clear();
        }
    }

    /**
     * Values collection of the map.
     */
    private final class Values extends AbstractValues {
        /** {@inheritDoc} */
        @Override public Iterator<V> iterator() {
            return new ValueIterator(true);
        }
    }

    /**
     * Values collection of the map.
     */
    private final class ValuesDescending extends AbstractValues {
        /** {@inheritDoc} */
        @Override public Iterator<V> iterator() {
            return new ValueIterator(false);
        }
    }

    /**
     * Entry set implementation.
     */
    private abstract class AbstractEntrySet extends AbstractSet<Map.Entry<K, V>> {
        /** {@inheritDoc} */
        @Override public boolean contains(Object o) {
            if (!(o instanceof Map.Entry))
                return false;

            Map.Entry<?, ?> e = (Map.Entry<?, ?>)o;

            V v = get(e.getKey());

            return v != null && v.equals(e.getValue());
        }

        /** {@inheritDoc} */
        @Override public boolean remove(Object o) {
            if (!(o instanceof Map.Entry))
                return false;

            Map.Entry<?, ?> e = (Map.Entry<?, ?>)o;

            return ConcurrentLinkedHashMap.this.remove(e.getKey(), e.getValue());
        }

        /** {@inheritDoc} */
        @Override public int size() {
            return ConcurrentLinkedHashMap.this.size();
        }

        /** {@inheritDoc} */
        @Override public void clear() {
            ConcurrentLinkedHashMap.this.clear();
        }
    }

    /**
     * Entry set implementation.
     */
    private final class EntrySet extends AbstractEntrySet {
        /** {@inheritDoc} */
        @Override public Iterator<Map.Entry<K, V>> iterator() {
            return new EntryIterator(true);
        }
    }

    /**
     * Entry set implementation.
     */
    private final class EntrySetDescending extends AbstractEntrySet {
        /** {@inheritDoc} */
        @Override public Iterator<Map.Entry<K, V>> iterator() {
            return new EntryIterator(false);
        }
    }

    /**
     * Defines queue policy for this hash map.
     */
    @SuppressWarnings("PublicInnerClass")
    public enum QueuePolicy {
        /**
         * Default policy. Single queue is maintained. Iteration order is preserved.
         */
        SINGLE_Q,

        /**
         * Instance of {@code ArrayDeque} is created for each segment. This gives
         * the fastest &quot;natural&quot; evicts for bounded maps.
         * <p>
         * NOTE: Remove operations on map are slower than with other policies.
         * <p>
         * NOTE: Iteration order is not preserved, i.e. iteration goes as if it was ordinary hash map.
         */
        PER_SEGMENT_Q,

        /**
         * Instance of {@code GridConcurrentLinkedDequeue} is created for each segment. This gives
         * faster &quot;natural&quot; evicts for bounded queues and better remove operation times.
         * <p>
         * NOTE: Iteration order is not preserved, i.e. iteration goes as if it was ordinary hash map.
         */
        PER_SEGMENT_Q_OPTIMIZED_RMV
    }
}
