Java8 set of source code analysis framework --LinkedHashMap

The paper is as follows:

A, LinkedHashMap the Javadoc documentation comments and a brief description

  First of worship LinkedHashMap Javadoc, the only say admire this document annotation LinkedHashMap the main features are listed out. Notes understand this, then the control source, you can understand a 7788 eight separate ways summary does not say so many strange, where's all. Here are some of Javadoc excerpt:

  • LinkedHashMap hash table and the linked list is a class that implements the Map interface, all internal node maintains a doubly linked list, the prediction iteration order, the default iteration insertion order outputs (k existing put back does not affect the order, because m.containsKey (k ) will first return true), this feature required for orderly Map parameter is useful and efficient than TreeMap.
  • LinkedHashMap also provides a configuration for access to the specified output in order to iterate, i.e., the least recently accessed according to the access order of most recently accessed: from least-recently accessed to most-recently (access-order). This feature is suitable for cache LRU (least-recently used cache), that is inherited LinkedHashMap, rewrite removeEldestEntry (Map.Entry) method to specify when the policy removed.
  • LinkedHashMap inherited HashMap, basic operations (add, contains and remove) may be considered to be O (1), due to the need to maintain the doubly-linked lists, performance may be slightly lower than the HashMap, but with one exception: LinkedHashMap iteration only the size of the actual ( after all, you can rely on iterative doubly linked list), and HashMap iteration is related to the capacity, performance will be relatively lower than LinkedHashMap.
  • Also not suitable for multi-threaded operations, the need for additional synchronization, such as the use Collections.synchronizedMap.
  • Iterator is fail-fast, and there is no guarantee that occurs concurrent modification throw ConcurrentModificationException hundred percent, but as far as possible to check, and therefore only suitable for the detection bug (throw ConcurrentModificationException indicate a problem, but not thrown out can not explain no problem).

  As can be seen, LinkedHashMap has two main purposes:

  • Ordered HashMap
  • LRU

LinkedHashMap 的 Javadoc:

/**
 * <p>Hash table and linked list implementation of the <tt>Map</tt> interface,
 * with predictable iteration order.  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 normally the order in which keys were inserted into the map
 * (<i>insertion-order</i>).  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>This implementation spares its clients from the unspecified, generally
 * chaotic ordering provided by {@link HashMap} (and {@link Hashtable}),
 * without incurring the increased cost associated with {@link TreeMap}.  It
 * can be used to produce a copy of a map that has the same order as the
 * original, regardless of the original map's implementation:
 * <pre>
 *     void foo(Map m) {
 *         Map copy = new LinkedHashMap(m);
 *         ...
 *     }
 * </pre>
 * This technique is particularly useful if a module takes a map on input,
 * copies it, and later returns results whose order is determined by that of
 * the copy.  (Clients generally appreciate having things returned in the same
 * order they were presented.)
 *
 * <p>A special {@link #LinkedHashMap(int,float,boolean) constructor} is
 * provided to create a linked hash map whose order of iteration is the order
 * in which its entries were last accessed, from least-recently accessed to
 * most-recently (<i>access-order</i>).  This kind of map is well-suited to
 * building LRU caches.  Invoking the {@code put}, {@code putIfAbsent},
 * {@code get}, {@code getOrDefault}, {@code compute}, {@code computeIfAbsent},
 * {@code computeIfPresent}, or {@code merge} methods results
 * in an access to the corresponding entry (assuming it exists after the
 * invocation completes). The {@code replace} methods only result in an access
 * of the entry if the value is replaced.  The {@code putAll} method generates one
 * entry access for each mapping in the specified map, in the order that
 * key-value mappings are provided by the specified map's entry set iterator.
 * <i>No other methods generate entry accesses.</i>  In particular, operations
 * on collection-views do <i>not</i> affect the order of iteration of the
 * backing map.
 *
 * <p>The {@link #removeEldestEntry(Map.Entry)} method may be overridden to
 * impose a policy for removing stale mappings automatically when new mappings
 * are added to the map.
 *
 * <p>This class provides all of the optional <tt>Map</tt> operations, and
 * permits null elements.  Like <tt>HashMap</tt>, it provides constant-time
 * performance for the basic operations (<tt>add</tt>, <tt>contains</tt> and
 * <tt>remove</tt>), assuming the hash function disperses elements
 * properly among the buckets.  Performance is likely to be just slightly
 * below that of <tt>HashMap</tt>, due to the added expense of maintaining the
 * linked list, with one exception: Iteration over the collection-views
 * of a <tt>LinkedHashMap</tt> requires time proportional to the <i>size</i>
 * of the map, regardless of its capacity.  Iteration over a <tt>HashMap</tt>
 * is likely to be more expensive, requiring time proportional to its
 * <i>capacity</i>.
 *
 * <p>A linked hash map has two parameters that affect its performance:
 * <i>initial capacity</i> and <i>load factor</i>.  They are defined precisely
 * as for <tt>HashMap</tt>.  Note, however, that the penalty for choosing an
 * excessively high value for initial capacity is less severe for this class
 * than for <tt>HashMap</tt>, as iteration times for this class are unaffected
 * by capacity.
 *
 * <p><strong>Note that this implementation is not synchronized.</strong>
 * If multiple threads access a linked hash map concurrently, and at least
 * one of the threads modifies the map structurally, it <em>must</em> be
 * synchronized externally.  This is typically accomplished by
 * synchronizing on some object that naturally encapsulates the map.
 *
 * If no such object exists, the map should be "wrapped" using the
 * {@link Collections#synchronizedMap Collections.synchronizedMap}
 * method.  This is best done at creation time, to prevent accidental
 * unsynchronized access to the map:<pre>
 *   Map m = Collections.synchronizedMap(new LinkedHashMap(...));</pre>
 *
 * A structural modification is any operation that adds or deletes one or more
 * mappings or, in the case of access-ordered linked hash maps, affects
 * iteration order.  In insertion-ordered linked hash maps, merely changing
 * the value associated with a key that is already contained in the map is not
 * a structural modification.  <strong>In access-ordered linked hash maps,
 * merely querying the map with <tt>get</tt> is a structural modification.
 * </strong>)
 *
 * <p>The iterators returned by the <tt>iterator</tt> method of the collections
 * returned by all of this class's collection view methods are
 * <em>fail-fast</em>: if the map is structurally modified at any time after
 * the iterator is created, in any way except through the iterator's own
 * <tt>remove</tt> method, the iterator will throw a {@link
 * ConcurrentModificationException}.  Thus, in the face of concurrent
 * modification, the iterator fails quickly and cleanly, rather than risking
 * arbitrary, non-deterministic behavior at an undetermined time in the future.
 *
 * <p>Note that the fail-fast behavior of an iterator cannot be guaranteed
 * as it is, generally speaking, impossible to make any hard guarantees in the
 * presence of unsynchronized concurrent modification.  Fail-fast iterators
 * throw <tt>ConcurrentModificationException</tt> on a best-effort basis.
 * Therefore, it would be wrong to write a program that depended on this
 * exception for its correctness:   <i>the fail-fast behavior of iterators
 * should be used only to detect bugs.</i>
 *
 * <p>The spliterators returned by the spliterator method of the collections
 * returned by all of this class's collection view methods are
 * <em><a href="Spliterator.html#binding">late-binding</a></em>,
 * <em>fail-fast</em>, and additionally report {@link Spliterator#ORDERED}.
 *
 * <p>This class is a member of the
 * <a href="{@docRoot}/../technotes/guides/collections/index.html">
 * Java Collections Framework</a>.
 *
 * @implNote
 * The spliterators returned by the spliterator method of the collections
 * returned by all of this class's collection view methods are created from
 * the iterators of the corresponding collections.
 *
 * @param <K> the type of keys maintained by this map
 * @param <V> the type of mapped values
 *
 * @author  Josh Bloch
 * @see     Object#hashCode()
 * @see     Collection
 * @see     Map
 * @see     HashMap
 * @see     TreeMap
 * @see     Hashtable
 * @since   1.4
 */

  

Second, the implementation of internal LinkedHashMap: extends attribute constructors and

  LinkedHashMap inherited HashMap, where the focus LinkedHashMap expanded in terms of internal properties, and constructors said lower part.

1, expanded attributes, and an internal class

  We can initially see some internal changes, such as increasing the recording head node and the end node of the internal node element increases before and after the node. These are needed to maintain the doubly-linked list used. The other is accessOrder, in accordance with the access order to specify whether (set to true) sort (the default is false insertion order).

    /**
     * HashMap.Node subclass for normal LinkedHashMap entries.
     * LinkedHashMap internal node implementation class, where an increase of the before and after the node for maintaining doubly-linked list
     * Here inherited HashMap.Node, to ensure consistent type of the new node, it is HashMap.Node
     */
    static class Entry<K,V> extends HashMap.Node<K,V> {
        Entry<K,V> before, after;
        Entry(int hash, K key, V value, Node<K,V> next) {
            super(hash, key, value, next);
        }
    }

    /**
     * The head (eldest) of the doubly linked list.
     * The first element node (the first insertion / least recently visited)
     */
    transient LinkedHashMap.Entry<K,V> head;

    /**
     * The tail (youngest) of the doubly linked list.
     * End node element (latest insertion / recently visited)
     */
    transient LinkedHashMap.Entry<K,V> tail;

    /**
     * The iteration ordering method for this linked hash map: <tt>true</tt>
     * for access-order, <tt>false</tt> for insertion-order.
     * Iterator sequence control
     * True: according to Access Order
     * False: default scene, according to the order of insertion
     * @serial
     */
    final boolean accessOrder;

  

2, constructors

  And HashMap constructor difference is mainly accessOrder setting.

    /**
     * Constructs an empty insertion-ordered <tt>LinkedHashMap</tt> instance
     * with the specified initial capacity and load factor.
     *
     * Specified initial capacity and load factor, while the default for the intervening sequence
     * @param  initialCapacity the initial capacity
     * @param  loadFactor      the load factor
     * @throws IllegalArgumentException if the initial capacity is negative
     *         or the load factor is nonpositive
     */
    public LinkedHashMap(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor);
        accessOrder = false;
    }

    /**
     * Constructs an empty insertion-ordered <tt>LinkedHashMap</tt> instance
     * with the specified initial capacity and a default load factor (0.75).
     *
     * Specifies the initial capacity, the default load factor 0.75, while the default for the intervening sequence
     * @param  initialCapacity the initial capacity
     * @throws IllegalArgumentException if the initial capacity is negative
     */
    public LinkedHashMap(int initialCapacity) {
        super(initialCapacity);
        accessOrder = false;
    }

    /**
     * Constructs an empty insertion-ordered <tt>LinkedHashMap</tt> instance
     * with the default initial capacity (16) and load factor (0.75).
     *
     * Empty constructor, the default initial capacity 16, the default load factor of 0.75, while the default for the intervening sequence
     */
    public LinkedHashMap() {
        super();
        accessOrder = false;
    }

    /**
     * Constructs an insertion-ordered <tt>LinkedHashMap</tt> instance with
     * the same mappings as the specified map.  The <tt>LinkedHashMap</tt>
     * instance is created with a default load factor (0.75) and an initial
     * capacity sufficient to hold the mappings in the specified map.
     *
     * The default is specified by sequentially inserted LinkedHashMap configured Map
     * @param  m the map whose mappings are to be placed in this map
     * @throws NullPointerException if the specified map is null
     */
    public LinkedHashMap(Map<? extends K, ? extends V> m) {
        super();
        accessOrder = false;
        putMapEntries(m, false);
    }

    /**
     * Constructs an empty <tt>LinkedHashMap</tt> instance with the
     * specified initial capacity, load factor and ordering mode.
     *
     * Specified initial capacity, load factor, ordering mode
     * @param  initialCapacity the initial capacity
     * @param  loadFactor      the load factor
     * @param  accessOrder     the ordering mode - <tt>true</tt> for
     *         access-order, <tt>false</tt> for insertion-order
     * @throws IllegalArgumentException if the initial capacity is negative
     *         or the load factor is nonpositive
     */
    public LinkedHashMap(int initialCapacity,
                         float loadFactor,
                         boolean accessOrder) {
        super(initialCapacity, loadFactor);
        this.accessOrder = accessOrder;
    }

  

Three, LinkedHashMap the put operation and expansion

  put operate directly inherited from HashMap, due to the processing involves LinkedHashMap doubly linked list, here are a few pay attention to point / point it should be noted that under the changes:

1, to create a function to rewrite the new node Node <K, V> newNode (int hash, K key, V value, Node <K, V> e), linked list maintenance bis

  LinkedHashMap node will be doubly linked list, so here are processed, it is clear that even if the new node is the last access date inserted directly to the final throw nothing wrong, and therefore linked to the final / at the latest list.

// create a new node and the new node to the last link 
the Node <K, V> the newNode ( int the hash, Key K, V value, the Node <K, V> E) {
    LinkedHashMap.Entry<K,V> p =
        new LinkedHashMap.Entry<K,V>(hash, key, value, e);
    linkNodeLast (the p-);         // new node is linked to the last 
    return the p-;
}

// Link AT The End of List
 // new node to the last link 
Private  void linkNodeLast (LinkedHashMap.Entry <K, V> P) {
    LinkedHashMap.Entry<K,V> last = tail;
    tail = p;
    if (last == null)
        head = p;
    else {
        p.before = last;
        last.after = p;
    }
}

  

2, HashMap to stay in the three callbacks, LinkedHashMap are rewritten

  put into operation, there is used afterNodeAccess (Node <K, V> p) and afterNodeInsertion (boolean evict).

  • afterNodeAccess (Node <K, V> p): present operation performed when k. If the access control procedure based on, you need to be linked to the access node to the end;
  • afterNodeInsertion (boolean evict): k when there is no operation performed. LRU cache can operate the actual node is removed
// Callbacks to the allow a LinkedHashMap POST-Actions 
void afterNodeAccess (the Node <K, V> P) {}             // operating the access node needs if access control in accordance with a specified sequence, then moved to the last node where the 
void afterNodeInsertion ( Boolean the evict) {}        // operation after the insertion node is required, such as to remove the oldest LRU cache node 
void afterNodeRemoval (the node <K, V> P) {}            // Removes the specified node

   LinkedHashMap implemented as follows:

// removing operation after the node element e, for HashMap, removeNode function has been removed node, here LinkedHashMap processing node and related to a doubly linked list before and After 
void afterNodeRemoval (the Node <K, V> e) { // the unlink 
    LinkedHashMap.Entry <K, V> P = 
        (LinkedHashMap.Entry <K, V>) e, p.before = B, a = p.after;
     // remove e link node itself 
    p.before = = p.after null ;
     IF (B == null )         // reset a node after the node e link 
        head = a;
     the else 
        b.after = a;
     IF (a == null )         //E resets the next link of a node before 
        tail = B;
     the else 
        a.before = B;
}

// whether to remove the first insertion / access node elements 
void afterNodeInsertion ( boolean evict) { // Possibly the Remove eldest 
    LinkedHashMap.Entry <K, V> First;
     // easiest LRU cache is actually rewrite removeEldestEntry returns true when logic (such as over the capacity limit), and then removing the first insert / access node 
    IF (&& the evict (= first head)! = null && the removeEldestEntry (first)) {
        K key = first.key;
        removeNode(hash(key), key, null, false, true);
    }
}

// whether the last access node after the node moved 
void afterNodeAccess (the Node <K, V> E) { // Move to Node Last 
    LinkedHashMap.Entry <K, V> Last;
     IF ! (AccessOrder && (Last = tail) = e) {
        LinkedHashMap.Entry<K,V> p =
            (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
        p.after = null ;
         IF (B == null )         // reset a node after the node e link 
            head = A;
         the else 
            b.after = A;
         IF (! A = null )         // reset the node e a link node before 
            a.before = B;
         the else 
            Last = B;
         IF (Last == null )         // scene only a node e of 
            head = P;
         the else {
            p.before = Last;     // the last moved node e 
            last.after = P;
        }
        tail = P;             // End node processing 
        ++ ModCount;
    }
}

   Look at here removeEldestEntry (Map.Entry <K, V> eldest), this method is the key to achieving LRU cache is, in fact, has stated documentation comments brief application, that is, whether the actual size of the check Map longer than the stated capacity More than that returns true, the nodes need to be removed to ensure the collection does not exceed the specified limit.

/**
 * Returns <tt>true</tt> if this map should remove its eldest entry.
 * This method is invoked by <tt>put</tt> and <tt>putAll</tt> after
 * inserting a new entry into the map.  It provides the implementor
 * with the opportunity to remove the eldest entry each time a new one
 * is added.  This is useful if the map represents a cache: it allows
 * the map to reduce memory consumption by deleting stale entries.
 *
 * <p>Sample use: this override will allow the map to grow up to 100
 * entries and then delete the eldest entry each time a new entry is
 * added, maintaining a steady state of 100 entries.
 * <pre>
 *     private static final int MAX_ENTRIES = 100;
 *
 *     protected boolean removeEldestEntry(Map.Entry eldest) {
 *        return size() &gt; MAX_ENTRIES;
 *     }
 * </pre>
 *
 * <p>This method typically does not modify the map in any way,
 * instead allowing the map to modify itself as directed by its
 * return value.  It <i>is</i> permitted for this method to modify
 * the map directly, but if it does so, it <i>must</i> return
 * <tt>false</tt> (indicating that the map should not attempt any
 * further modification).  The effects of returning <tt>true</tt>
 * after modifying the map from within this method are unspecified.
 *
 * <p>This implementation merely returns <tt>false</tt> (so that this
 * map acts like a normal map - the eldest element is never removed).
 *
 * @param    eldest The least recently inserted entry in the map, or if
 *           this is an access-ordered map, the least recently accessed
 *           entry.  This is the entry that will be removed it this
 *           method returns <tt>true</tt>.  If the map was empty prior
 *           to the <tt>put</tt> or <tt>putAll</tt> invocation resulting
 *           in this invocation, this will be the entry that was just
 *           inserted; in other words, if the map contains a single
 *           entry, the eldest entry is also the newest.
 * @return   <tt>true</tt> if the eldest entry should be removed
 *           from the map; <tt>false</tt> if it should be retained.
 */
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
    return false;
}

 3, there is show a comparison operation is the internal HashMap red-black tree node is directly inherited TreeNode LinkedHashMap.Entry, so this regard red-black tree transformation, expansion and the like can be said to be substantially seamless.

static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {...}

  Red-black tree transformation and expansion is only related to the internal nodes move, doubly linked list is not altered, there is no need to operate.

 

Four, LinkedHashMap the get operation

  Increased afterNodeAccess (Node <K, V> p) calls for a LinkedHashMap access sequence control, need to be moved to the last node visited. And other HashMap same.

    /**
     * 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==null ? k==null :
     * key.equals(k))}, then this method returns {@code v}; otherwise
     * it returns {@code null}.  (There can be at most one such mapping.)
     *
     * <p>A return value of {@code null} does not <i>necessarily</i>
     * indicate that the map contains no mapping for the key; it's also
     * possible that the map explicitly maps the key to {@code null}.
     * The {@link #containsKey containsKey} operation may be used to
     * distinguish these two cases.
     */
    public V get(Object key) {
        Node<K,V> e;
        if ((e = getNode(hash(key), key)) == null)
            return null;
        if (accessOrder)
            afterNodeAccess (E);         // operating the access node needs to increase, if the access sequence in accordance with the specified control is moved to the last node where the 
        return e.Value;
    }

  

Five, LinkedHashMap remove operation

  Node is removed using the HashMap remove (Object key), it is in fact remove the same, only the processing required at the end LinkedHashMap doubly linked list, as used herein, is to extend the afterNodeRemoval (Node <K, V> p) to be treated . This method can achieve the look LinkedHashMap introduced earlier in this article.

Guess you like

Origin www.cnblogs.com/wpbxin/p/12185050.html