从源码理解LinkedHashMap.java

本文深入剖析了Java中的LinkedHashMap,一种按插入顺序或访问顺序维护迭代顺序的哈希表和链表实现。LinkedHashMap通过双向链表保持元素顺序,并介绍了如何通过特定构造函数实现最近最少使用的缓存。
摘要由CSDN通过智能技术生成
package java.util;

import java.util.function.Consumer;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.io.IOException;

import HashMap.Node;

/**
 * 哈希表和链表实现Map接口,并具有固定迭代顺序。
 * LinkedHashMap实现和HashMap的不同之处在于LinkedHashMap维护了一个双向链表来存储所有的Entry。
 * 这个链表定义了迭代顺序,即关键字插入Map的顺序。重复插入关键字不会影响顺序。
 * Hashtable和HashMap提供无序的键值对。
 * 有一个特殊的构造函数LinkedHashMap(int,float,boolean) constructor用于创建一个链式Hash Map,它的迭代顺序是Entry上次访问顺序,
 * 从最近访问最少到最近访问最多。非常适合建立LRU caches。
 * 集合视图上的操作不会影响Map的遍历。
 * removeEldestEntry(Map.Entry)允许重写自定义规则,在新映射添加的时候自动移除一些映射。
 * 该类实现了Map的所有可选操作,允许空null值。
 * HashMap提供常数时间的基本操作(add, contains, remove),假设哈希函数将元素均匀的散列在各槽中。
 * 由于需要维护链表,LinkedHashMap的性能略低于HashMap。
 * 但有一个例外:在LinkedHashMap的集合视图中遍历的时间和Size成正比,和capacity无关,
 * 而HashMap的集合视图中遍历的时间和capacity成正比。
 * 
 * LinkedHashMap有两个参数影响其性能:初始容量initial capacity,装载因子load factor,HashMap也是如此。
 *
 * 该实现也是非线程安全的。多线程访问时,需要外部同步。例如,需要Collections#synchronizedMap Collections.synchronizedMap方法包装:
 * Map m = Collections.synchronizedMap(new LinkedHashMap(...));
 * 在以访问决定顺序的链式HashMap中只有get是结构上的操作。
 * LinkedHashMap的集合视图也都是快速失败的。
 * 
 * 底层使用哈希表与双向链表来保存所有元素,其基本操作与父类HashMap相似,它通过重写父类相关的方法来实现自己的链表特性。
 * 即LinkedHashMap的结点有:final int hash;   //不可变的哈希值————由关键字key得来
        				final K key;		//关键字不可变 
        				V value;
        				Node<K,V> next;
        				Entry<K,V> before;
        				Entry<K,V> after;	
 */
public class LinkedHashMap<K,V>
    extends HashMap<K,V>
    implements Map<K,V>
{
    /*
     * LinkedHashMap的结点Entry也可以转换成树的形式。
     * 结点类中使用(head,tail)来维护一个双向链表(before,after)。
     * 回调函数的风格也不太一样。
     */

    /**
     * LinkedHashMap的Entry类型继承自HashMap.Node 
     *  该Entry除了保存当前对象的引用外,还保存了其上一个元素before和下一个元素after的引用
     *  从而在哈希表的基础上又构成了双向链表
     */
    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);
        }
    }

    private static final long serialVersionUID = 3801124242820219131L;

    /**
     * 双向链表的头结点(最早的结点).
     */
    transient LinkedHashMap.Entry<K,V> head;

    /**
     * 双向链表的尾结点(最晚的结点).
     */
    transient LinkedHashMap.Entry<K,V> tail;

    /**
     * LinkedHashMap的迭代顺序:true是访问顺序(即最近最少使用次序),false是插入顺序
     */
    final boolean accessOrder;

    // 内部工具
    // 链到链表末端
    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;
        }
    }

    // apply src's links to dst
    	/**
    	 * 将src的链接链到dst上————用dst替换src
    	 */
    private void transferLinks(LinkedHashMap.Entry<K,V> src,
                               LinkedHashMap.Entry<K,V> dst) {
        LinkedHashMap.Entry<K,V> b = dst.before = src.before;
        LinkedHashMap.Entry<K,V> a = dst.after = src.after;
        	//分别考虑首尾情况
        		//src是头结点
        if (b == null)
            head = dst;
        else
            b.after = dst;
        		//src是尾结点
        if (a == null)
            tail = dst;
        else
            a.before = dst;
    }

    // 重写HashMap的方法 
    void reinitialize() {
        super.reinitialize();
        	//重新初始化head,tail,均为null
        head = tail = null;
    }
    //创建新节点并链接到尾部
    Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
        LinkedHashMap.Entry<K,V> p =
            new LinkedHashMap.Entry<K,V>(hash, key, value, e);
        	//将p链到尾部
        linkNodeLast(p);
        return p;
    }
    //替换结点,用next替换p
    Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) {
        LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p;
        LinkedHashMap.Entry<K,V> t =
            new LinkedHashMap.Entry<K,V>(q.hash, q.key, q.value, next);
        	//用t替换q
        transferLinks(q, t);
        return t;
    }

    TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {
        TreeNode<K,V> p = new TreeNode<K,V>(hash, key, value, next);
        linkNodeLast(p);
        return p;
    }

    TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) {
        LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p;
        TreeNode<K,V> t = new TreeNode<K,V>(q.hash, q.key, q.value, next);
        transferLinks(q, t);
        return t;
    }
    //删除结点e后调整
    void afterNodeRemoval(Node<K,V> e) { // 解链
        LinkedHashMap.Entry<K,V> p =
            (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
        p.before = p.after = null;
        	//分别考虑首尾情况
        if (b == null)
            head = a;
        else
            b.after = a;
        if (a == null)
            tail = b;
        else
            a.before = b;
    }

    void afterNodeInsertion(boolean evict) { // 可能需要移除最老结点
        LinkedHashMap.Entry<K,V> first;
        	//removeEldestEntry(first)始终返回false,所以这里移除结点后不会有操作,不会移除最老结点
        	//这样做的目的是方便开发者可以把Map当cache来用,并且可以限制大小,只需继承LinkedHashMap并重写removeEldestEntry()
        if (evict && (first = head) != null && removeEldestEntry(first)) {
            K key = first.key;
            removeNode(hash(key), key, null, false, true);
        }
    }
    /**
     * 当accessOrder为true(按最近最少使用)时,结点访问(get,put操作)后需要调整结点顺序,
     * 将当前被操作节点移动到head结点之前,即链表的尾部
     */
    void afterNodeAccess(Node<K,V> e) { // move node to last
        LinkedHashMap.Entry<K,V> last;
        	//先将结点e从原链表中解除出来,再链到head之前
        if (accessOrder && (last = tail) != e) {
            LinkedHashMap.Entry<K,V> p =
                (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
            //需要分别考虑p结点是head和tail的情况
            	//断开p=e与其后继结点的关联
            p.after = null;
            	//若p=e的前一个结点为空,则p为原头结点head,所以删除后,p的原后继结点a成为新的头结点head
            if (b == null)
                head = a;
            	//p不是头结点,则b的后继指向a结点
            else
                b.after = a;
            	//后继结点a不为空,a指向p的前驱结点b
            if (a != null)
                a.before = b;
            	//后继结点为空,则p结点为尾结点tail,所以删除后,p的前驱成为新的尾结点
            else
                last = b;
            	//若last=b为空,则p为头结点,现在
            if (last == null)
                head = p;
            	//last不为空
            else {
                p.before = last;
                last.after = p;
            }
            	//将p结点链到尾部
            tail = p;
            ++modCount;
        }
    }

    void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException {
        for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) {
            s.writeObject(e.key);
            s.writeObject(e.value);
        }
    }

    /**
     * 构造方法1:购造一个指定初始容量和负载因子的、按照插入顺序的LinkedHashMap
     * @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) {
    		//调用父类HashMap的相关构造方法来构造一个底层存放的table数组,
    		//但额外可以增加accessOrder这个参数,若不设置,默认false,代表按插入顺序迭代
    		//true为按访问顺序进行迭代
    		//HashMap的构造器中会调用init()方法,进行相关的初始化,这个方法在HashMap的实现中并无意义,只是提供给子类实现相关的初始化调用
        super(initialCapacity, loadFactor);
        accessOrder = false;
    }

    /**
     * 构造方法2:构造一个指定初始容量的LinkedHashMap,取得键值对的顺序是插入顺序,默认装载因子0.75
     * @param  initialCapacity the initial capacity
     * @throws IllegalArgumentException if the initial capacity is negative
     */
    public LinkedHashMap(int initialCapacity) {
        super(initialCapacity);
        accessOrder = false;
    }

    /**
     * 构造方法3:用默认的初始化容量16和负载因子0.75创建一个LinkedHashMap,取得键值对的顺序是插入顺序
     */
    public LinkedHashMap() {
        super();
        accessOrder = false;
    }

    /**
     * 构造方法4:通过传入的Map创建一个LinkedHashMap,容量为默认容量(16)和(map.size()/DEFAULT_LOAD_FACTORY)+1的较大者,、
     * 装载因子为默认值0.75
     * @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);
    }

    /**
     * 构造方法5:根据指定容量、装载因子和键值对保持顺序创建一个LinkedHashMap
     * @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;
    }


    /**
     * 若有一个以上关键字映射到该值,返回true
     * 重写父类的containsValue(Object value)方法,直接通个关header遍历链表判断是否有值和value相等
     * 不用查询table数组
     * @param value 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
     */
    public boolean containsValue(Object value) {
    		//通过header遍历链表判断是否有值和value相等
        for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) {
            V v = e.value;
            if (v == value || (value != null && value.equals(v)))
                return true;
        }
        return false;
    }

    /**
     * 返回指定关键字映射到的值,不存在则返回null
     */
    public V get(Object key) {
        Node<K,V> e;
        if ((e = getNode(hash(key), key)) == null)
            return null;
        	//如果按照访问顺序(accessOrder为true),需要调整结点顺序
        if (accessOrder)
            afterNodeAccess(e);
        return e.value;
    }

    /**
     * {@inheritDoc}
     */
    public V getOrDefault(Object key, V defaultValue) {
       Node<K,V> e;
       if ((e = getNode(hash(key), key)) == null)
           return defaultValue;
       if (accessOrder)
           afterNodeAccess(e);
       return e.value;
   }

    /**
     * 清空LinkedHashMap
     */
    public void clear() {
    		//调用父类的方法clear()
        super.clear();
        	//将链表的header结点的before和after引用都指向null
        	//这样就无法访问到原链表中剩余的其他结点,他们都将被GC回收
        head = tail = null;
    }

    /**
     * 如果Map需要移除最老结点,返回true
     * 这样做的目的是方便开发者可以把Map当cache来用,并且可以限制大小,只需继承LinkedHashMap并重写removeEldestEntry()
     * put和putAll方法之后需要调用该函数
     * <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() > 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;
    }

    /**
     * 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.  If the map is modified
     * while an iteration over the set is in progress (except through
     * the iterator's own <tt>remove</tt> operation), the results of
     * the iteration are undefined.  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.
     * Its {@link Spliterator} typically provides faster sequential
     * performance but much poorer parallel performance than that of
     * {@code HashMap}.
     *
     * @return a set view of the keys contained in this map
     */
    public Set<K> keySet() {
        Set<K> ks;
        return (ks = keySet) == null ? (keySet = new LinkedKeySet()) : ks;
    }

    final class LinkedKeySet extends AbstractSet<K> {
        public final int size()                 { return size; }
        public final void clear()               { LinkedHashMap.this.clear(); }
        public final Iterator<K> iterator() {
            return new LinkedKeyIterator();
        }
        public final boolean contains(Object o) { return containsKey(o); }
        public final boolean remove(Object key) {
            return removeNode(hash(key), key, null, false, true) != null;
        }
        public final Spliterator<K> spliterator()  {
            return Spliterators.spliterator(this, Spliterator.SIZED |
                                            Spliterator.ORDERED |
                                            Spliterator.DISTINCT);
        }
        public final void forEach(Consumer<? super K> action) {
            if (action == null)
                throw new NullPointerException();
            int mc = modCount;
            for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
                action.accept(e.key);
            if (modCount != mc)
                throw new ConcurrentModificationException();
        }
    }

    /**
     * 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.  If the map is
     * modified while an iteration over the collection is in progress
     * (except through the iterator's own <tt>remove</tt> operation),
     * the results of the iteration are undefined.  The collection
     * supports element removal, which removes the corresponding
     * mapping from the 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.
     * Its {@link Spliterator} typically provides faster sequential
     * performance but much poorer parallel performance than that of
     * {@code HashMap}.
     *
     * @return a view of the values contained in this map
     */
    public Collection<V> values() {
        Collection<V> vs;
        return (vs = values) == null ? (values = new LinkedValues()) : vs;
    }

    final class LinkedValues extends AbstractCollection<V> {
        public final int size()                 { return size; }
        public final void clear()               { LinkedHashMap.this.clear(); }
        public final Iterator<V> iterator() {
            return new LinkedValueIterator();
        }
        public final boolean contains(Object o) { return containsValue(o); }
        public final Spliterator<V> spliterator() {
            return Spliterators.spliterator(this, Spliterator.SIZED |
                                            Spliterator.ORDERED);
        }
        public final void forEach(Consumer<? super V> action) {
            if (action == null)
                throw new NullPointerException();
            int mc = modCount;
            for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
                action.accept(e.value);
            if (modCount != mc)
                throw new ConcurrentModificationException();
        }
    }

    /**
     * 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.  If the map is modified
     * while an iteration over the set is in progress (except through
     * the iterator's own <tt>remove</tt> operation, or through the
     * <tt>setValue</tt> operation on a map entry returned by the
     * iterator) the results of the iteration are undefined.  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.
     * Its {@link Spliterator} typically provides faster sequential
     * performance but much poorer parallel performance than that of
     * {@code HashMap}.
     *
     * @return a set view of the mappings contained in this map
     */
    public Set<Map.Entry<K,V>> entrySet() {
        Set<Map.Entry<K,V>> es;
        return (es = entrySet) == null ? (entrySet = new LinkedEntrySet()) : es;
    }

    final class LinkedEntrySet extends AbstractSet<Map.Entry<K,V>> {
        public final int size()                 { return size; }
        public final void clear()               { LinkedHashMap.this.clear(); }
        public final Iterator<Map.Entry<K,V>> iterator() {
            return new LinkedEntryIterator();
        }
        public final boolean contains(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<?,?> e = (Map.Entry<?,?>) o;
            Object key = e.getKey();
            Node<K,V> candidate = getNode(hash(key), key);
            return candidate != null && candidate.equals(e);
        }
        public final boolean remove(Object o) {
            if (o instanceof Map.Entry) {
                Map.Entry<?,?> e = (Map.Entry<?,?>) o;
                Object key = e.getKey();
                Object value = e.getValue();
                return removeNode(hash(key), key, value, true, true) != null;
            }
            return false;
        }
        public final Spliterator<Map.Entry<K,V>> spliterator() {
            return Spliterators.spliterator(this, Spliterator.SIZED |
                                            Spliterator.ORDERED |
                                            Spliterator.DISTINCT);
        }
        public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
            if (action == null)
                throw new NullPointerException();
            int mc = modCount;
            for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
                action.accept(e);
            if (modCount != mc)
                throw new ConcurrentModificationException();
        }
    }

    // Map overrides

    public void forEach(BiConsumer<? super K, ? super V> action) {
        if (action == null)
            throw new NullPointerException();
        int mc = modCount;
        for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
            action.accept(e.key, e.value);
        if (modCount != mc)
            throw new ConcurrentModificationException();
    }

    public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
        if (function == null)
            throw new NullPointerException();
        int mc = modCount;
        for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
            e.value = function.apply(e.key, e.value);
        if (modCount != mc)
            throw new ConcurrentModificationException();
    }

    // Iterators

    abstract class LinkedHashIterator {
        LinkedHashMap.Entry<K,V> next;
        LinkedHashMap.Entry<K,V> current;
        int expectedModCount;

        LinkedHashIterator() {
            next = head;
            expectedModCount = modCount;
            current = null;
        }

        public final boolean hasNext() {
            return next != null;
        }

        final LinkedHashMap.Entry<K,V> nextNode() {
            LinkedHashMap.Entry<K,V> e = next;
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            if (e == null)
                throw new NoSuchElementException();
            current = e;
            next = e.after;
            return e;
        }

        public final void remove() {
            Node<K,V> p = current;
            if (p == null)
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            current = null;
            K key = p.key;
            removeNode(hash(key), key, null, false, false);
            expectedModCount = modCount;
        }
    }

    final class LinkedKeyIterator extends LinkedHashIterator
        implements Iterator<K> {
        public final K next() { return nextNode().getKey(); }
    }

    final class LinkedValueIterator extends LinkedHashIterator
        implements Iterator<V> {
        public final V next() { return nextNode().value; }
    }

    final class LinkedEntryIterator extends LinkedHashIterator
        implements Iterator<Map.Entry<K,V>> {
        public final Map.Entry<K,V> next() { return nextNode(); }
    }


}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值