java容器 类LinkedHashMap源码分析

目录

简介

节点类 Entry

字段 head,tail,accessOrder

覆盖HashMap的方法 reinitialize,newNode2个,replacement2个

afterNodeXXX3个,internalWriteEntries,removeEldestEntry

构造函数5个

containsKey,get两个,clear

LInkedHashMap的get和put方法造成的结果

集合视图  3个set

forEach,replaceAll

4个迭代器


简介

/**
 * 

哈希表和链表,实现了Map接口,具有可预测的迭代顺序。 * 这个实现与HashMap的不同之处在于,它维护了一个双链表,该列表遍历其所有条目。 * 这个链表定义了迭代顺序,它通常是键插入到映射中的顺序(插入顺序)。 * 注意,如果一个键被重新插入到映射中,插入顺序不会受到影响。 * (已经有key了,再put(key,value))。 * *

这个实现使它的用户免于HashMap(和Hashtable)提供的未指定的、混乱的排序, * 也不会增加与TreeMap相关的成本。 * 它可以用于生成与原始的map顺序相同的map副本,而不管原始map的实现: *

 *     void foo(Map m) {
 *         Map copy = new LinkedHashMap(m);
 *         ...
 *     }
 * 
* 如果一个功能先获取一个map作为输入,然后复制它, * 然后返回结果,其顺序由被复制的map的顺序决定,那么这种技术特别有用。 * (客户一般都喜欢按照收到礼物的顺序来退货。) * *

提供了一个特殊的构造函数LinkedHashMap(int,float,boolean), * 被提供来创建一个链接的散列映射,其迭代的顺序是其条目最后一次被访问的顺序,从最少被访问到最近被访问(访问顺序)。 * 这种map非常适合构建LRU缓存。(Least Recently Used,最近最少使用) * 调用put、putIfAbsent、get、getOrDefault、compute、computeIfAbsent、computeIfPresent或merge方法将导致对相应项的访问(假设在调用完成后存在)。 * 如果值被替换,则replace方法只会导致对条目的访问。 * putAll方法为指定映射中的每个映射生成一个条目访问,其顺序是由指定map的entrySet迭代器提供键-值映射。 * 没有其他方法生成条目访问。特别是,对集合视图的操作不会影响依赖的map的迭代顺序。 * *

可以重写removeEldestEntry(map. entry)方法,以便在向映射添加新映射时自动删除陈旧的映射。 * *

该类提供所有可选的映射操作,并允许空元素。 * 与HashMap一样,它为基本操作(添加、包含和删除)提供了固定时间的性能,假设散列函数正确地将元素分散到各个bucket中。 * 由于维护链表的额外开销,性能可能略低于HashMap,但有一个例外: * 对LinkedHashMap的集合视图的迭代需要与映射的大小成比例的时间,而与它的容量无关。 * HashMap的迭代可能更昂贵,需要的时间与它的容量成比例。 * *

一个链接的哈希映射有两个影响其性能的参数:初始容量和负载因子。 * 它们被精确地定义为HashMap。 * 但是,请注意,对于初始容量选择过高值的惩罚对于这个类来说没有HashMap那么严重,因为这个类的迭代时间不受容量的影响。 * *

注意,这个实现不是同步的。如果多个线程同时访问一个链接的散列映射,并且至少有一个线程从结构上修改了该映射,则必须在外部对其进行同步。 * 这通常是通过对一些自然封装了映射的对象进行同步来实现的。 * 如果不存在这样的对象,则应该使用Collections.synchronizedMap方法。这最好在创建时完成,以防止意外的不同步访问map:

 *   Map m = Collections.synchronizedMap(new LinkedHashMap(...));
* * 结构修改是添加或删除一个或多个映射的任何操作,对于基于访问顺序的链接哈希映射,它会影响迭代顺序。 * 在基于插入顺序的链接哈希映射中,仅更改已包含在映射中的键相关联的值不是结构性修改。 * 在基于访问顺序的链接哈希映射中,仅使用get查询映射是一种结构修改。 * 即对于基于插入顺序的,add,remove影响。 * 基于访问顺序的,add,remove,get都影响。 * *

这个类的集合视图,返回的迭代器是快速失败: * 如果创建迭代器后修改map的结构,除非通过迭代器的删除方法,迭代器将抛出ConcurrentModificationException。 * 因此,在面对并发修改时,迭代器会快速而干净地失败,而不是在将来某个不确定的时间冒任意的、不确定的行为的风险。 * *

注意,不能保证迭代器的快速故障行为,因为通常来说,在存在非同步并发修改的情况下,不可能做出任何严格的保证。 * 故障快速迭代器在最大努力的基础上抛出ConcurrentModificationException。 * 因此,编写一个依赖于这个异常的正确性的程序是错误的:迭代器的快速故障行为应该只用于检测bug。 * *

这个类的所有集合视图方法返回的集合的spliterator方法返回的spliterator是迟绑定的、快速失败的,另外还报告ORDERED。 * * 这个类的所有集合视图方法返回的集合的spliterator方法返回的spliterator是从相应集合的迭代器创建的。 * * @param the type of keys maintained by this map * @param the type of mapped values * * @author Josh Bloch * @see Object#hashCode() * @see Collection * @see Map * @see HashMap * @see TreeMap * @see Hashtable * @since 1.4 */ public class LinkedHashMap extends HashMap implements Map

java容器 类LinkedHashMap源码分析_第1张图片

节点类 Entry

   /*
     * 实现注释。这个类以前的版本在内部结构上略有不同。
     * 因为超类HashMap现在对它的一些节点使用树节点。
     * 类LinkedHashMap.Entry现在被视为中间节点类,也可以将其转换为树形式。
     * 这个类的名字,LinkedHashMap.Entry,在当前的上下文中,在几个方面令人困惑,但是不能更改。
     * 否则,即使它没有导出到这个包之外,已知的一些现有源代码在调用removeEldestEntry时依赖于符号的解析规则,
     * 该规则抑制了由于含糊不清的用法而导致的编译错误。因此,我们保留这个名称以保持未修改的编译性。
     *
     * 节点类中的更改还需要使用两个字段(head和tail)
     * 而不是指向头节点的指针来维护双链接的before/after列表。
     * 这个类以前还使用了不同风格的回调方法在访问,插入和删除。
     */

    /**
     * HashMap.Node 的子类,应用于普通的 LinkedHashMap 的节点。
     */
    static class Entry extends HashMap.Node {
    	// node有 key,value, hash,next
    	// linkedlist的entry,增加了before,after
    	// TreeNode增加了 parent,left,right,prev,red
        Entry before, after;
        Entry(int hash, K key, V value, Node next) {
            super(hash, key, value, next);
        }
    }

字段 head,tail,accessOrder

    /**
     * 双端队列的头结点(最老的节点)
     */
    transient LinkedHashMap.Entry head;

    /**
     * 双端队列的尾结点(最新的节点)
     */
    transient LinkedHashMap.Entry tail;

    /**
     * 这个链接的哈希映射的迭代顺序:对于访问顺序为true,对于插入顺序为false。
     * 默认为false,插入顺序。
     * @serial
     */
    final boolean accessOrder;

内部工具方法 linkNodeLast,transferLinks

    // 内部工具方法

    // 设置双端队列的尾部为p
    private void linkNodeLast(LinkedHashMap.Entry p) {
        LinkedHashMap.Entry last = tail;
        tail = p;
        if (last == null)
            head = p;
        else {
            p.before = last;
            last.after = p;
        }
    }

    // 就是dst替换src的链接关系,原来b  src  a 变成  b  dst  a
    // 调用replacementNode和replacementTreeNode时调用这个方法
    // 普通节点与树节点转换时,调用
    private void transferLinks(LinkedHashMap.Entry src,
                               LinkedHashMap.Entry dst) {
    	// 设置src的before,after给dst,现在是b  dst   a
        LinkedHashMap.Entry b = dst.before = src.before;
        LinkedHashMap.Entry a = dst.after = src.after;
        // 设置a,b,head,tail节点
        if (b == null)
            head = dst;
        else
            b.after = dst;
        if (a == null)
            tail = dst;
        else
            a.before = dst;
    }

覆盖HashMap的方法 reinitialize,newNode2个,replacement2个

    // overrides of HashMap hook methods

    void reinitialize() {
        super.reinitialize();
        // 设置head和tail
        head = tail = null;
    }

    // hashmap的putVal方法,最后如果不是新增节点,而是对已经存在节点进行修改,调用afterNodeAccess
    // 如果是新增节点,会调用linkedhashmap的newNode方法,里面将新增节点放到队列尾部
    Node newNode(int hash, K key, V value, Node e) {
        LinkedHashMap.Entry p =
            new LinkedHashMap.Entry(hash, key, value, e);
        // 设置双端队列的尾部为p
        linkNodeLast(p);
        return p;
    }

    Node replacementNode(Node p, Node next) {
        LinkedHashMap.Entry q = (LinkedHashMap.Entry)p;
        LinkedHashMap.Entry t =
            new LinkedHashMap.Entry(q.hash, q.key, q.value, next);
        transferLinks(q, t);
        return t;
    }

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

    TreeNode replacementTreeNode(Node p, Node next) {
        LinkedHashMap.Entry q = (LinkedHashMap.Entry)p;
        TreeNode t = new TreeNode(q.hash, q.key, q.value, next);
        transferLinks(q, t);
        return t;
    }

afterNodeXXX3个,internalWriteEntries,removeEldestEntry

    // hashmap的removeNode方法,最后调用afterNodeRemoval(node)
    void afterNodeRemoval(Node e) { // 删除链接
        LinkedHashMap.Entry p =
            (LinkedHashMap.Entry)e, b = p.before, a = p.after;
        // 顺序 b  p  a
        // 设置p的before和after
        p.before = p.after = null;
        // 设置head或者b的after
        if (b == null)
            head = a;
        else
            b.after = a;
        // 设置tail或者a的before
        if (a == null)
            tail = b;
        else
            a.before = b;
    }

    // hashmap的putVal方法在最后,调用afterNodeInsertion(evict);
    // evict 如果false,哈希表处于创建阶段。
    void afterNodeInsertion(boolean evict) { // 可能删除最老元素
        LinkedHashMap.Entry first;
        // 如果不是创建阶段,head不为null,removeEldestEntry返回true,删除first(LinkedHashMap不删除)
        if (evict && (first = head) != null && removeEldestEntry(first)) {
            K key = first.key;
            removeNode(hash(key), key, null, false, true);
        }
    }

    // hashmap的putVal方法,最后如果不是新增节点,而是对已经存在节点进行修改,调用afterNodeAccess
    // 如果是新增节点,会调用linkedhashmap的newNode方法,里面将新增节点放到队列尾部
    // linkedhashmap的get方法,最后如果为访问顺序,调用afterNodeAccess
    void afterNodeAccess(Node e) { // 将节点移动到最后
        LinkedHashMap.Entry last;
        if (accessOrder && (last = tail) != e) {
        	// 如果是访问顺序,而且tail不是e
        	// 赋值tail给last,e给p
        	// b和a是p的before和after
            LinkedHashMap.Entry p =
                (LinkedHashMap.Entry)e, b = p.before, a = p.after;
            // 设置p的after
            p.after = null;
            // 设置head或者b的after
            if (b == null)
                head = a;
            else
                b.after = a;
            // 设置a的before
            if (a != null)
                a.before = b;
            else
            	// 如果a为null,设置last为b
                last = b;
            // 设置head或者p的before和last的after
            if (last == null)
                head = p;
            else {
                p.before = last;
                last.after = p;
            }
            // 设置tail
            tail = p;
            ++modCount;
        }
    }

    void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException {
        for (LinkedHashMap.Entry e = head; e != null; e = e.after) {
        	// 从head开始循环
            s.writeObject(e.key);
            s.writeObject(e.value);
        }
    }
    /**
     * 如果此map应该删除其最老的条目,则返回true。
     * 在向映射中插入新条目之后,put和putAll将调用此方法。
     * 它为实现者提供了在每次添加新条目时删除最老条目的机会。
     * 如果映射表示一个缓存,这是很有用的:它允许映射通过删除陈旧的条目来减少内存消耗。
     *
     * 

示例使用:这个覆盖将允许映射增长到100个条目, * 然后在每次添加新条目时删除最老的条目,从而保持100个条目的稳定状态。 *

     *     private static final int MAX_ENTRIES = 100;
     *
     *     protected boolean removeEldestEntry(Map.Entry eldest) {
     *        return size() > MAX_ENTRIES;
     *     }
     * 
* *

此方法通常不以任何方式修改映射,而是允许映射根据其返回值的方向修改自身。 * (即 if(removeEldestEntry(eldest)) remove(eldest) ) * 允许此方法直接修改映射,但如果这样做,则必须返回false(指示映射不应尝试任何进一步修改)。 * 在此方法中修改映射后返回true的效果是未指定的。 * *

这个实现只返回false(因此这个映射就像一个普通的映射——最年长的元素永远不会被删除)。 * * @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 true. If the map was empty prior * to the put or putAll 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 true if the eldest entry should be removed * from the map; false if it should be retained. */ protected boolean removeEldestEntry(Map.Entry eldest) { return false; }

构造函数5个

    /**
     * 使用指定的初始容量和负载因子构造一个按插入顺序排列的空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的方法
        super(initialCapacity, loadFactor);
        // 默认为false,插入顺序
        accessOrder = false;
    }

    /**
     * C使用指定的初始容量和默认的负载因子(0.75)构造一个按插入顺序排列的空LinkedHashMap实例。
     *
     * @param  initialCapacity the initial capacity
     * @throws IllegalArgumentException if the initial capacity is negative
     */
    public LinkedHashMap(int initialCapacity) {
        super(initialCapacity);
        accessOrder = false;
    }

    /**
     * 使用默认初始容量(16)和负载因子(0.75)构造一个按插入顺序排列的空LinkedHashMap实例。
     */
    public LinkedHashMap() {
        super();
        accessOrder = false;
    }

    /**
     * 使用与指定映射相同的映射,构造按插入顺序排列的LinkedHashMap实例。
     * LinkedHashMap实例是使用默认的负载因子(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 m) {
        super();
        accessOrder = false;
        putMapEntries(m, false);
    }

    /**
     * 使用指定的初始容量、负载因子和排序模式构造一个空的LinkedHashMap实例。
     *
     * @param  initialCapacity the initial capacity
     * @param  loadFactor      the load factor
     * @param  accessOrder     the ordering mode - true for
     *         access-order, false 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;
    }

containsKey,get两个,clear

    /**
     * 如果此映射将一个或多个键映射到指定的值,则返回true。
     *
     * @param value value whose presence in this map is to be tested
     * @return true if this map maps one or more keys to the
     *         specified value
     */
    public boolean containsValue(Object value) {
        for (LinkedHashMap.Entry e = head; e != null; e = e.after) {
        	// 从head开始,不断e = e.after循环
            V v = e.value;
            if (v == value || (value != null && value.equals(v)))
                return true;
        }
        return false;
    }

    /**
     * 返回指定键映射到的值,如果该映射不包含键的映射,则返回null。
     *
     * 

更正式地说,如果这个映射包含一个从键k到值v的映射(key==null ?k==null: key.equals(k)), * 则该方法返回v;否则返回null。(最多可以有一个这样的映射。) * *

返回值为null并不一定表示映射不包含键的映射;映射也可能显式地将键映射为null。 * containsKey操作可以用来区分这两种情况。 * * */ public V get(Object key) { Node e; // 先调用hashmap的getNode方法 if ((e = getNode(hash(key), key)) == null) return null; if (accessOrder) // 如果为访问顺序,调用afterNodeAccess afterNodeAccess(e); return e.value; } /** * {@inheritDoc} */ public V getOrDefault(Object key, V defaultValue) { Node e; if ((e = getNode(hash(key), key)) == null) return defaultValue; if (accessOrder) afterNodeAccess(e); return e.value; } /** * {@inheritDoc} */ public void clear() { super.clear(); // head和tail为null head = tail = null; }

LInkedHashMap的get和put方法造成的结果

默认的构造器,accessOrder = false,除非使用LinkedHashMap(int initialCapacity,float loadFactor,boolean accessOrder)

get方法,如果accessOrder=true,调用afterNodeAccess方法

afterNodeAccess(Node e)方法,如果accessOrder=true,而且tail不是e,将e节点移动到最后

put方法,hashmap的putVal方法,最后如果不是新增节点,而是对已经存在节点进行修改,调用afterNodeAccess,如果是新增节点,会调用linkedhashmap的newNode方法,newNode方法里面将新增节点放到队列尾部

HashMap.Node 有 key,value, hash,next

LinkedHashMap.Entry extends HashMap.Node ,增加了before,after

TreeNode extends LinkedHashMap.Entry ,增加了 parent,left,right,prev,red

 

集合视图  3个set

    /**
     * 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 remove operation), the results of
     * the iteration are undefined.  The set supports element removal,
     * which removes the corresponding mapping from the map, via the
     * Iterator.remove, Set.remove,
     * removeAll, retainAll, and clear
     * operations.  It does not support the add or addAll
     * 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 keySet() {
        Set ks = keySet;
        if (ks == null) {
            ks = new LinkedKeySet();
            keySet = ks;
        }
        return ks;
    }

    final class LinkedKeySet extends AbstractSet {
        public final int size()                 { return size; }
        public final void clear()               { LinkedHashMap.this.clear(); }
        public final Iterator 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 spliterator()  {
            return Spliterators.spliterator(this, Spliterator.SIZED |
                                            Spliterator.ORDERED |
                                            Spliterator.DISTINCT);
        }
        public final void forEach(Consumer action) {
            if (action == null)
                throw new NullPointerException();
            int mc = modCount;
            for (LinkedHashMap.Entry e = head; e != null; e = e.after)
            	// 从head开始循环
                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 remove operation),
     * the results of the iteration are undefined.  The collection
     * supports element removal, which removes the corresponding
     * mapping from the map, via the Iterator.remove,
     * Collection.remove, removeAll,
     * retainAll and clear operations.  It does not
     * support the add or addAll 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 values() {
        Collection vs = values;
        if (vs == null) {
            vs = new LinkedValues();
            values = vs;
        }
        return vs;
    }

    final class LinkedValues extends AbstractCollection {
        public final int size()                 { return size; }
        public final void clear()               { LinkedHashMap.this.clear(); }
        public final Iterator iterator() {
            return new LinkedValueIterator();
        }
        public final boolean contains(Object o) { return containsValue(o); }
        public final Spliterator spliterator() {
            return Spliterators.spliterator(this, Spliterator.SIZED |
                                            Spliterator.ORDERED);
        }
        public final void forEach(Consumer action) {
            if (action == null)
                throw new NullPointerException();
            int mc = modCount;
            for (LinkedHashMap.Entry 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 remove operation, or through the
     * setValue 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 Iterator.remove,
     * Set.remove, removeAll, retainAll and
     * clear operations.  It does not support the
     * add or addAll 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> entrySet() {
        Set> es;
        return (es = entrySet) == null ? (entrySet = new LinkedEntrySet()) : es;
    }

    final class LinkedEntrySet extends AbstractSet> {
        public final int size()                 { return size; }
        public final void clear()               { LinkedHashMap.this.clear(); }
        public final Iterator> 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 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> spliterator() {
            return Spliterators.spliterator(this, Spliterator.SIZED |
                                            Spliterator.ORDERED |
                                            Spliterator.DISTINCT);
        }
        public final void forEach(Consumer> action) {
            if (action == null)
                throw new NullPointerException();
            int mc = modCount;
            for (LinkedHashMap.Entry e = head; e != null; e = e.after)
                action.accept(e);
            if (modCount != mc)
                throw new ConcurrentModificationException();
        }
    }

forEach,replaceAll

    // Map overrides

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

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

4个迭代器

    // Iterators

    abstract class LinkedHashIterator {
        LinkedHashMap.Entry next;
        LinkedHashMap.Entry current;
        int expectedModCount;

        LinkedHashIterator() {
        	// next最初为head
            next = head;
            expectedModCount = modCount;
            current = null;
        }

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

        final LinkedHashMap.Entry nextNode() {
            LinkedHashMap.Entry e = next;
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            if (e == null)
                throw new NoSuchElementException();
            current = e;
            // 递归时,next=next.after
            next = e.after;
            return e;
        }

        public final void remove() {
            Node 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;
        }
    }

    // 下面与hashmap的一样,基于nextNode方法
    final class LinkedKeyIterator extends LinkedHashIterator
        implements Iterator {
        public final K next() { return nextNode().getKey(); }
    }

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

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

你可能感兴趣的:(源码分析,java容器)