Java 数据结构 -- 16.Java 8 数据结构 LinkedHashMap

本文详细介绍了HashMap的子类LinkedHashMap,强调了其恒定顺序的特性,并通过源码分析解释了如何通过双向链表实现这一特性。LinkedHashMap在插入、删除和访问节点时会根据预设顺序进行调整,并且迭代器直接遍历链表,而非HashMap的数组和桶。下篇将探讨TreeMap。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

书接上文,上一篇中对 Map 接口的 最终实现类 HashMap 进行了介绍与分析,本篇将对 HashMap 的子类,实现了恒定顺序特性的 LinkedHashMap 进行介绍与分析。

这里就不再放图了,直接来看 LinkedHashMap 的源码

/**
 * Map 接口的 Hash table 和链接链表实现,伴随一个可预测的迭代操作顺序(恒等顺序)。这个实现与 HashMap 不
 * 同的地方在于维护了一个覆盖它的所有条目的双向链表。这个链接链表定义了迭代操作的顺序,通常是键s加入 map 的顺
 * 序(iteration-order)。注意迭代操作顺序不会手动重新加入 map 的键s的影响。(如果对于键 k,map m 在 
 * m.containsKey(k) 返回是 true 的时候马上调用 m.put(k, v) 那么 k 就是重新加入的。)
 *
 * 这个实现类省去了它的客户端从一个 {@link HashMap} (and {@link Hashtable}) 中不确定的,通常是混乱的顺
 * 序的打扰,而不会带来增加的有关 {@link TreeMap} 的性能消耗。他可以被用来产生一个与原始的,不用考虑原始 
 * map 的实现的,比如
 * <pre>
 *     void foo(Map m) {
 *         Map copy = new LinkedHashMap(m);
 *         ...
 *     }
 * </pre>
 * 的保持相同顺序的拷贝。
 *
 * 这个技术主要在一个以 map 为输入的模块,拷贝它,然后在稍后的时间返回一个顺序被拷贝对象定义的拷贝时有用。
 * (客户端通常对于它们提供的有相同顺序的东西心怀感激。)
 *
 * 一个特殊的 {@link #LinkedHashMap(int,float,boolean) constructor} 被提供来建立一个迭代操作顺序与
 * 最终访问顺序(access-order)相同的链接 hash map。这种 map 很适合建立 LRU 缓存。调用 {@code put}, 
 * {@code putIfAbsent}, {@code get}, {@code getOrDefault}, {@code compute}, {@code 
 * computeIfAbsent}, {@code computeIfPresent}, or {@code merge} 方法返回一个对应条目的访问(加入它
 * 在调用结束后存在)。{@code replace} 方法只在一个条目的值被替换时候返回访问。{@code putAll} 方法生为指
 * 定 map 中的每一个映射生成一个条目访问,以指定 map 的 entry set 迭代器提供的键值映射的顺序。没有其他方法
 * 生成条目访问。特别要指出的是,对于 collection 视图的操作不会影响提供支持的 map 的迭代操作。
 *
 * {@link #removeEldestEntry(Map.Entry)} 方法可以被重写来强制执行一个策略,在新的映射加入到 map 中时
 * 自动地移除过期的映射。
 *
 * 这个类提供所有可选的 Map 操作,并且允许 null 元素。就像 HashMap,它对于基础操作(add,contains 和 
 * remove)提供一个时间恒定的性能,假设 hash 功能很好的将元素分散到了各个桶中。性能看上去只比 HashMap 低一
 * 点,因为要加入维护链接链表的成本,伴随一个异常:对于一个 LinkedHashMap 的 collection 视图的迭代操作需
 * 要与 map 长度成比例的时间,不用考虑它的容量。涵盖一个 HashMap 的迭代操作看上去会更昂贵,需要与容量成比例
 * 的时间。
 * 
 * 一个链接 hash map 有两个要素会影响它的性能:
 * initial capacity(初始化容量)和 load factor(加载因子)。它们就像 HashMap 那样被精确地定义。注意,
 * 但是,选择过高的初始化容量值的惩罚对比 HashMap,这个类没有这么严重,因为对于这个类的迭代操作时间是不受容
 * 量影响的。
 *
 * 注意这个实现类不是线程安全的。如果多个线程同事访问一个 linked hash map,并且至少一个线程结构性地修改了 
 * map,它必须从外部实现线程安全。这通常是通过在一些对象中自然封装这个 map 来完成的。
 *
 * 如果这样的对象不存在,map 应该使用
 * {@link Collections#synchronizedMap Collections.synchronizedMap} 
 * 方法来“封装”。这最好在创建的时候做,来避免对于这个 map 意外的非线程安全的访问:<pre>
 *   Map m = Collections.synchronizedMap(new LinkedHashMap(...));</pre>
 *
 * 一个结构化的修改是任何添加或者删除一个或多个映射操作,在 access-ordered linked hash maps 的情况中,
 * 会影响迭代操作的顺序。在 insertion-ordered linked hash maps 中,仅仅改变 map 中已经存在键的值不是一
 * 个结构性改变。在 access-ordered linked hash maps 中,仅仅通过 get 查询 map 不是一个结构性改变。
 *
 * 所有这个类的 colleciton 视图返回的 collections 的 iterator 方法返回的迭代器都是 fail-fast 的:如果
 * 在迭代操作创建后的任何时间点这个 map 被结构性的更改了,以除了通过迭代器自己的 remove 方法之外的任何方
 * 式。迭代器将抛出一个  {@link ConcurrentModificationException}。因此,对于并发修改,迭代器失败的快速
 * 与干净,而不是在未来某个不确定的时间点冒险做任何不确定的行为。
 *
 * 注意一个迭代器的 fail-fast 行为不能提供像它所描述的那样的保证,一般来说,不可能对于不同步的并发操作做任何
 * 硬性的保证。基于最好性能的基础,fail-fast 迭代器抛出一个 ConcurrentModificationException。因此,编
 * 写一个依赖于这个异常来保证其正确性的程序是错误的:迭代器的 fail-fast 行为应该只被用于检查 bugs。
 *
 * 通过这个类的 collections 视图返回的 coillection 的 spliterator 方法返回的并行迭代器是延迟绑定,
 * fail-fast,和另外记录的{@link Spliterator#ORDERED}。
 *
 * LinkedHashMap 继承自 HashMap,实现了 Map 接口。
 *
 * 注意本类中全程看不到 table 与 tab,但是它还是 HashMap 散列表特性的,说明它直接使用了 HashMap 的底层结
 * 构,而没有改动,也就是数组结构(桶,可能是一个 Node,可能是一个 Node 链表,也可能是一颗红黑 Node 树),
 * 所谓的 Linked 链接的就是忽略了桶的种类,将所有的条目双向链接在一起而已。
 *
 * 和 HashMap 相同的部分已经在 HashMap 篇中做过介绍,这里只翻译与解释不同的部分。
 */
public class LinkedHashMap<K,V>
    extends HashMap<K,V>
    implements Map<K,V>
{

    /*
     * 实现类注意。这个类与它的前置版本在内部结构上有一点小差异。因为父类 HashMap 现在对于一些它的 nodes 
     * 使用 tress,LinkedHashMap.Entry 类现在被当做中间 node 类对待使它也可以转换成 tree 的形式。这个
     * 类的名字,LinkedHashMap.Entry,在它当前的上下文中在多个方面都费解的,但是不能被改变了。不然的话,
     * 即使他没有在当前包外部被传播,一些已知的依赖代码会发生错误(这一段太长了不是很好翻译,就简写了)。所
     * 以,我们保持名称来保存未修改的可编译性。
     * 
     * node 类s的改变也需要使用到两个类属性(head,tail)而不是一个指向 header node 的指针来维护双向链表 
     * before/after 链表。这个类先前也是在访问,插入和移除时使用使用不同风格的回调方法。

    /**
     * 面向普通 LinkedHashMap 条目s 的 HashMap.Node 子类,继承自 HashMap.Node。
     */
    static class Entry<K,V> extends HashMap.Node<K,V> {
        Entry<K,V> before, after; //新增了 before 与 after 属性,注意这里的 after 与 next 是两个属性,为了与 next(后节点区分开),一下对于 before 与 after 的称呼为前条目和后条目,head 与 tail 的称呼为头条目与尾条目
        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;

    /**
     * 当前 linked hash map 的迭代操作排序方法:
     * 对于 access-order true
     * 对于 insertion-order false
     */
    final boolean accessOrder;

    // 内部工具

    // 链接在链表尾部
    private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
        LinkedHashMap.Entry<K,V> last = tail;
        tail = p;
        if (last == null) //如果当前 LinkedHashMap 的尾条目为 null
            head = p; //p 赋值给头条目
        else { //如果当前 LinkedHashMap 的尾条目不为 null
            p.before = last; //链接参数条目与原来的尾条目
            last.after = p; //链接原来的尾巴条目与参数条目
        }
    }

    // 应用 src 的链接s到 dst
    private void transferLinks(LinkedHashMap.Entry<K,V> src,
                               LinkedHashMap.Entry<K,V> dst) {
        LinkedHashMap.Entry<K,V> b = dst.before = src.before; //获取 src 的前条目关系,更新给 dst
        LinkedHashMap.Entry<K,V> a = dst.after = src.after; //获取 src 的后条目关系,更新给 dst
        if (b == null)//如果前条目为 null
            head = dst; //dst 就是头条目
        else //如果前条目不为 null
            b.after = dst; //更新 b 的链接关系,将 dsc 链接到 src 条目前条目的后面
        if (a == null) //如果后条目为 null
            tail = dst; //dst 就是尾条目
        else //如果后条目不为 null
            a.before = dst; //更新 a 的链接关系,将 dst 连接到 src 条目后条目的前面
    }

    // 重写 HashMap 钩子方法
		//重新初始化方法
    void reinitialize() { 
        super.reinitialize(); //调用 HashMap 的 reinitialize 方法
        head = tail = null; //头条目与尾条目置为 null
    }

		//获取新 Node 方法
    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); //构造新 LinkedHashMap 条目
        linkNodeLast(p); //调用 linkNodeLast 方法
        return p; //返回 p
    }

		//替换 Node 方法
    Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) {
        LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p; //强转参数 p
        LinkedHashMap.Entry<K,V> t =
            new LinkedHashMap.Entry<K,V>(q.hash, q.key, q.value, next); //构造新 LinkedHashMap 条目
        transferLinks(q, t); //调用 transferLinks 方法
        return t; //返回 t
    }

		//获取新的 TreeNode 方法
    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); //构造新 TreeNode
        linkNodeLast(p); //调用 linkNodLast 方法
        return p; //返回 p
    }

		//替换 TreeNode 方法
    TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) {
        LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p; //强转参数 p
        TreeNode<K,V> t = new TreeNode<K,V>(q.hash, q.key, q.value, next); //构造新 TreeNode
        transferLinks(q, t); //调用 transferLinks 方法
        return t;
    }

		//注意一下三个方法对应 HashMap 中提到的回调方法,所以这里不是条目,而是 Node,就是这三个方法保证了 HashMap 的双向链接特性
		//移除 Node 后方法
    void afterNodeRemoval(Node<K,V> e) { // 断开链接
        LinkedHashMap.Entry<K,V> p =
            (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; //强转参数,缓存强转后的 LinkedHashMap 条目,以及前条目和后条目
        p.before = p.after = null; //p 的前后节点置为 null
        //更新链接关系,与 transferLinks 一样
        if (b == null) 
            head = a;
        else
            b.after = a;
        if (a == null)
            tail = b;
        else
            a.before = b;
    }

		//在 Node 添加后方法
		//evict 的含义在 HashMap 篇中有解释,就是是否初始化构造 Map
    void afterNodeInsertion(boolean evict) { // 存在移除最古老节点的可能性
        LinkedHashMap.Entry<K,V> first;
        if (evict && (first = head) != null && removeEldestEntry(first)) { //如果是初始化构造 LinkedHashMap 并且头条目不为 null 并且调用 removeEldestEntry 返回 true
            K key = first.key; //键为头条目的键
            removeNode(hash(key), key, null, false, true); //调用 HashMap 的 removeNode 方法
        }
    }

		//在 Node 访问后方法
    void afterNodeAccess(Node<K,V> e) { // 移动 Node 到最后(调整最近访问链)
        LinkedHashMap.Entry<K,V> last;
        if (accessOrder && (last = tail) != e) { //如果是 accessOrder 并且尾条目不为参数
            LinkedHashMap.Entry<K,V> p =
                (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; //强转参数为 LinkedHashMap 条目,同时缓存该条目的前条目和后条目
            p.after = null; //后条目置为 null
            if (b == null)
                head = a;
            else
                b.after = a;
            if (a != null)
                a.before = b;
            else
                last = b; //更新链接关系,前面都与 transferLinks 一样,直到这一步,如果参数条目的后条目为 null,那么将参数前条目赋值给 last
            if (last == null) //如果 last 为 null,说明参数条目不存在前条目
                head = p; //将参数条目赋值给头条目
            else { //如果 last 不为 null,说明参数条目前条目存在
                p.before = last; //将参数条目的前条目位置 last
                last.after = p; //last 的后条目置为参数条目(这不是跟之前一样么。。)
            }
            tail = p; //尾条目置为参数条目
            ++modCount; //结构性变更,注意这里其实长度没有变更,只是变更了链接关系,自增 modCount
        }
    }

		//内部写条目方法,注意对应到 HashMap 的序列化写方法
    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); //写值
        }
    }

    /**
     * 构造一个带有指定初始化容量和加载因子的空的 insertion-ordered LinkedHashMap 实例。
     */
    public LinkedHashMap(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor); //调用 HashMap 的双参构造方法
        accessOrder = false; //accessOrder 置为 false
    }

    /**
     * 构造一个带有指定初始化容量和一个默认加载因子(0.75)的空的 insertion-ordered LinkedHashMap 实
     * 例。
     */
    public LinkedHashMap(int initialCapacity) {
        super(initialCapacity); //调用 HashMap 的单参构造方法
        accessOrder = false; //accessOrder 置为 false
    }

    /**
     * 构造一个带有默认初始化容量(16)和默认加载因子(0.75)的一个空的 insertion-ordered 
     * LinkedHashMap 实例。
     */
    public LinkedHashMap() {
        super(); //调用 HashMap 的无参构造方法
        accessOrder = false; //accessOrder 置为 false
    }

    /**
     * 构造一个与指定参数带有相同映射s的一个 insertion-ordered LinkedHashMap 实例。这个 
     * LinkedHashMap 实例被用一个默认加载影子(0.75)和一个足够容纳指定 map 中所有映射大小的初始化容量创
     * 建。
     */
    public LinkedHashMap(Map<? extends K, ? extends V> m) {
        super(); //调用 HashMap 的无参构造方法
        accessOrder = false; //accessOrder 置为 false
        putMapEntries(m, false); //调用 HashMap#putMapEntries 方法
    }

    /**
     * 构造一个带有指定初始化容量,加载因子和排序模式的空的 LinkedHashMap 实例。
     */
    public LinkedHashMap(int initialCapacity,
                         float loadFactor,
                         boolean accessOrder) {
        super(initialCapacity, loadFactor); //调用 HashMap 的双参构造方法
        this.accessOrder = accessOrder; //将 accessOrder 参数赋值给当前 LinkedHashMap 实例的 accessOrder
    }


    /**
     * 如果当前 map 映射到对应指定值的一个或多个键。
     */
    public boolean containsValue(Object value) {
        for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) { //循环 LinkedHashMap 条目
            V v = e.value;
            if (v == value || (value != null && value.equals(v)))
                return true; //如果返现相同条目,返回 true
        }
        return false; //否则返回 false
    }

    /**
     * 返回指定键对应的值,或者如果当前 map 不包含对于这个 key 的映射,返回 {@code null}。
     *
     * 更正式地说,如果 map 包含一个键 {@code k} 到一个值 {@code v} 存在 {@code (key==null ? 
     * k==null : key.equals(k))} 关系,那么这个方法返回 {@code v};不然的话它返回 {@code null}。
     * (只可能之多有一个这样的映射。)
     *
     * 一个返回值 {@code null} 不能明确地表示当前 map 不包含这个 key 所对应的映射;它也有可能是这个 map 
     * 明确地将键映射到了 {@code null}。{@link #containsKey containsKey} 操作可以被用来区分这两种情
     * 况。
     */
    public V get(Object key) {
        Node<K,V> e;
        if ((e = getNode(hash(key), key)) == null) //调用 HashMap#getNode
            return null;
        if (accessOrder) //如果是 accessOrder 的
            afterNodeAccess(e); //调用 afterNodeAccess,返回最新访问的 Node
        return e.value; //返回 e 的值
    }

    /**
     * 文档继承
     */
    public V getOrDefault(Object key, V defaultValue) {
       Node<K,V> e;
       if ((e = getNode(hash(key), key)) == null) //调用 HashMap#getNode
           return defaultValue; //如果返回为 null,返回 defaultValue
       if (accessOrder) //如果是 accessOrder 的
           afterNodeAccess(e); //调用 afterNodeAccess,返回最新访问的 Node
       return e.value; //返回 e 的值
   }

    /**
     * 文档继承
     */
    public void clear() {
        super.clear(); //调用 HashMap#clear
        head = tail = null; //头条目与尾条目置为 null
    }

    /**
     * 如果当前 map 应该移除它最老旧的条目则返回 true。这个方法在向当前 map 插入一个新的条目被 put 和 
     * putAll 调用。它为实现着提供了每次插入新条目时候移除最老旧条目的机会。这在如果 map 代表了一个缓存的时
     * 候有用:它允许 map 来通过删除陈腐的条目s来减少内存消费。
     *
     * 例子使用:这中重写将允许 map 增长到 100 个条目然后在每次一个新的条目被添加时删除最老旧的条目,维护一
     * 个稳定的 100 个条目的状态。
     * <pre>
     *     private static final int MAX_ENTRIES = 100;
     *
     *     protected boolean removeEldestEntry(Map.Entry eldest) {
     *        return size() > MAX_ENTRIES;
     *     }
     * </pre>
     *
     *。这个方法通常不一任何方式改动当前 map,而是允许 map 直接通过它的返回值修改它自己。它允许这个方法来直
     * 接修改 map,但是如果它这么做了,它必须返回 false(示意这个 map 不应该再尝试任何更多的改动)。在这个
     * 方法中改变这个 map 后返回 true 的影响是不可指定的。
     *
     * 这个实现仅仅返回 false(所以当前 map 就像一个普通 map 那样运作 - 最老旧的元素永远不会被移除)。
     */
    protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
        return false; //返回 false
    }

    /**
     * 返回键 Set 方法
     * 
     * 返回当前 map 包含的键s的一个 {@link Set} 视图。这个 set 是被这个 map 支持的,所有改变这个 map 
     * 会影响到这个 set,反之依然。如果这个 map 在一个涵盖 set 的迭代器操作过程中被改动了(除了通过迭代器
     * 自己的 remove 操作),迭代操作的返回值是不可定义的。这个 set 支持元素移除,就是从这个 map 中移除相
     * 应映射,通过 Iterator.remove,Set.remove,removeAll,retainAll,和 clear 操作。它不支持 add 
     * 或者 addAll 操作。
     * 
     * 它的 {@link Spliterator} 与 {@code HashMap} 比通常支持更快的顺序性能但是更低的并行性能。
     */
    public Set<K> keySet() {
        Set<K> ks = keySet; //这就是多态的好处了,KeySet 与 LinkeKeySet 都继承自 AbstractSet 实现了 Set 接口,有判断结果来选择返回哪一种 keySet
        if (ks == null) { //如果 HashMap 的 keySet 为 null
            ks = new LinkedKeySet(); //调用 LinkedKeySet 空构造器
            keySet = ks; //返回 LinkedKeySet 对象
        }
        return ks; //直接返回 KeySet 对象
    }

		/**
		 * LinkedKeySet 类,继承自 AbstractSet
		 **/
    final class LinkedKeySet extends AbstractSet<K> {
        public final int size()                 { return size; }
        public final void clear()               { LinkedHashMap.this.clear(); } //这里和 HashMap 不同,调用 LinkedHashMap 的 clear 方法
        public final Iterator<K> iterator() {
            return new LinkedKeyIterator(); //和 HashMap 不同,返回空的 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; //与 HashMap 的完全一样,但是由于是继承自 AbstractSet,所以需要再写一遍
        }
        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) //循环方式与 HashMap 不同
                action.accept(e.key);  //消费条目的键
            if (modCount != mc)
                throw new ConcurrentModificationException();
        }
    }

    /**
     * 返回一个包含当前 map 中所有值的 {@link Collection} 视图。
     *
     * 这个 collection 被这个 map 支持,所以对于这个 map 的修改会影响到这个 collection,反之亦然。如果
     * 这个 map 在一个涵盖 collection 的迭代器操作过程中被改动了(除了通过迭代器自己的 remove 操作),迭
     * 代操作的返回值是不可定义的。这个 collectin 支持元素移除,就是从这个 map 中移除相应映射,通过 
     * Iterator.remove,Set.remove,removeAll,retainAll,和 clear 操作。
     *
     * 它的 {@link Spliterator} 与 {@code HashMap} 比通常支持更快的顺序性能但是更低的并行性能。
     */
    public Collection<V> values() {
        Collection<V> vs = values;
        //与 LinkedKeySet 方法的逻辑一样
        if (vs == null) {
            vs = new LinkedValues(); //除了当 HashMap 的 values 不存在的时候调用的是空 LinkedValues 构造器
            values = vs;
        }
        return vs;
    }

		/**
		 * LinkedValues 类,继承自 AbstractCollection。
		 **/
    final class LinkedValues extends AbstractCollection<V> {
        public final int size()                 { return size; }
        public final void clear()               { LinkedHashMap.this.clear(); }
        //注意和 HashMap 的 Values 一样没有 remove() 方法
        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();
        }
    }

    /**
     * 返回一个包含了当前 map 中所有映射的 {@link Set} 视图。这个 set 是被这个 map 支持的,所有改变这个 
     * map 会影响到这个 set,反之依然。如果这个 map 在一个涵盖 set 的迭代器操作过程中被改动了(除了通过迭
     * 代器自己的 remove 操作),迭代操作的返回值是不可定义的。这个 set 支持元素移除,就是从这个 map 中移
     * 除相应映射,通过 Iterator.remove,Set.remove,removeAll,retainAll,和 clear 操作。它不支持 
     * add 或者 addAll 操作。
     */
    public Set<Map.Entry<K,V>> entrySet() {
        Set<Map.Entry<K,V>> es;
        return (es = entrySet) == null ? (entrySet = new LinkedEntrySet()) : es; //和 HashMap 不同,当 entrySet 为 null 时调用的是空 LinkedEntrySet 构造器
    }

		/**
		 * LinkedEntrySet 类,继承自 AbstractSet 类。
		 **/
    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) { //与 HashMap 完全一样,但是由于是继承自 AbstractSet 的,所以要再写一遍
            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 重写
		// Map forEach 消费方法
    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) //循环方式与 HashMap 不同
            action.accept(e.key, e.value); //消费键/值(void 方法,所以只是消费)
        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) //循环方式与 HashMap 不同
            e.value = function.apply(e.key, e.value); //对键/值应用函数调用(有返回值的方法,所以可以更新)
        if (modCount != mc)
            throw new ConcurrentModificationException();
    }

    // 迭代器
		// 可以看到是一种适配器模式
    abstract class LinkedHashIterator {
        LinkedHashMap.Entry<K,V> next; //下一个条目
        LinkedHashMap.Entry<K,V> current; //当前条目
        int expectedModCount; //防止同时修改异常
				//注意这里没有游标概念

        LinkedHashIterator() { //空构造器
            next = head; //头条目赋值给 next
            expectedModCount = modCount; //LinkedHashMap 的 modCount 赋值给 expectedModCount
            current = null; //null 赋值给 current
        }

        public final boolean hasNext() { //判断是否存在下一个
            return next != null; //判断 next 是否不为 null
        }

        final LinkedHashMap.Entry<K,V> nextNode() { //返回下一个条目
            LinkedHashMap.Entry<K,V> e = next; //缓存 next 给 e
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            if (e == null)
                throw new NoSuchElementException();
            current = e; //将 e 赋值给 current
            next = e.after; //更新 next 为 e 的后节点
            return e; //返回 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); //调用 HashMap removeNode 方法
            expectedModCount = modCount; //更新 exprectedModCount
        }
    }
		
		// LinkedKeyIterator 继承自 LinkedHashIterator,实现 Iterator 接口
    final class LinkedKeyIterator extends LinkedHashIterator
        implements Iterator<K> {
        public final K next() { return nextNode().getKey(); } //返回下一个条目的键
    }

		// LinkedValueIterator 继承自 LinkedHashIterator,实现 Iterator 接口
    final class LinkedValueIterator extends LinkedHashIterator
        implements Iterator<V> {
        public final V next() { return nextNode().value; } //返回下一个条目的值
    }

		// LinkedEntryIterator 继承自 LinkedHashIterator,实现 Iterator 接口
    final class LinkedEntryIterator extends LinkedHashIterator
        implements Iterator<Map.Entry<K,V>> {
        public final Map.Entry<K,V> next() { return nextNode(); } //返回下一个条目
    }


}

可以看到 LinkedHashMap 还是比较清楚的。它主要是对于 HashMap 中的 条目做了双向链表的实现。做法是通过在构造 LinkedHashMap 对象的时候选择合适的顺序(默认顺序,也就是插入顺序,或者访问顺序,也就是最近修改顺序),并通过实现在 HashMap 中预留的 #afterNodeRemoval(#removeNode),#afterNodeInsertion(对应 #putVal,#computeIfAbsent,#compute,#merge),#afterNodeAccess(#putVal,#replace,#computeIfAbsent,#computeIfPresent,#compute,#merge) 方法(这些方法中会根据顺序的类型来更新链接)来达到调整顺序的目的。
还要注意的是 LinkedHashMap 的迭代器不再是像 HashMap 那样遍历数组,再遍历桶了,而是直接循环链表。

以上就是对于 LinkedHashMap 的介绍与分析,下一篇将对 TreeMap 进行介绍与分析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值