LinkedHashMap的底层原理

LinkedHashMap 是 HashMap 的子类,在 HashMap 的哈希表结构基础上,通过 维护一个双向链表 实现了 顺序访问特性,支持 插入顺序(Insertion Order)访问顺序(Access Order)(如 LRU 策略)。以下是其底层原理的详细解析:


一、核心数据结构

LinkedHashMap 的底层结构由两部分组成:

  1. 哈希表
    继承自 HashMap,使用数组 + 链表/红黑树存储键值对,实现快速查找(O(1) 时间复杂度)。

  2. 双向链表
    每个节点(Entry)增加 beforeafter 指针,将所有节点按顺序串联,形成双向链表。
    头节点(head):链表最老的节点(插入或访问时间最早)。
    尾节点(tail):链表最新的节点(插入或访问时间最新)。

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);
    }
}

二、顺序维护机制

LinkedHashMap 支持两种顺序模式,通过 accessOrder 参数控制:
accessOrder = false(默认):按 插入顺序 维护链表。
accessOrder = true:按 访问顺序 维护链表(最近访问的节点移动到链表末尾)。

1. 插入顺序的实现

新节点插入链表尾部
重写 newNode() 方法,在创建新节点时将其链接到双向链表的末尾。

Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
    Entry<K,V> p = new Entry<>(hash, key, value, e);
    linkNodeLast(p); // 将节点链接到链表尾部
    return p;
}

链表维护逻辑

private void linkNodeLast(Entry<K,V> p) {
    Entry<K,V> last = tail;
    tail = p; // 新节点成为尾节点
    if (last == null) {
        head = p; // 链表为空时,头尾均指向新节点
    } else {
        p.before = last; // 前驱指向原尾节点
        last.after = p;  // 原尾节点的后继指向新节点
    }
}
2. 访问顺序的实现

访问节点后移动至链表尾部
重写 afterNodeAccess() 方法,在节点被访问(如 getput)后调整链表顺序。

void afterNodeAccess(Node<K,V> e) {
    Entry<K,V> last = tail;
    if (accessOrder && last != e) { // 需要移动且当前节点不是尾节点
        Entry<K,V> p = (Entry<K,V>)e;
        Entry<K,V> b = p.before, a = p.after;
        p.after = null;
        if (b == null) {
            head = a; // 移动头节点
        } else {
            b.after = a;
        }
        if (a != null) {
            a.before = b;
        } else {
            tail = b;
        }
        if (last == null) {
            head = p;
        } else {
            p.before = last;
            last.after = p;
        }
        tail = p; // 更新尾节点
    }
}

三、LRU 缓存支持

LinkedHashMap 可通过 覆盖 removeEldestEntry() 方法 实现 LRU(最近最少使用)缓存策略。
淘汰最旧节点:当元素数量超过容量时,删除链表头部的节点(最久未访问或最早插入的节点)。

示例代码
public class LRUCache<K, V> extends LinkedHashMap<K, V> {
    private final int maxCapacity;

    public LRUCache(int maxCapacity) {
        super(maxCapacity, 0.75f, true); // accessOrder = true
        this.maxCapacity = maxCapacity;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > maxCapacity; // 容量超限时删除最旧节点
    }
}

四、关键方法解析

  1. get() 方法
    • 调用 HashMap 的 getNode() 查找节点。
    • 若 accessOrder = true,触发 afterNodeAccess() 移动节点到链表尾部。

  2. put() 方法
    • 继承自 HashMap 的插入逻辑。
    • 插入新节点时调用 linkNodeLast() 链接到链表尾部。
    • 若覆盖已有键值对,触发 afterNodeAccess()(若 accessOrder = true)。

  3. remove() 方法
    • 继承自 HashMap 的删除逻辑。
    • 删除节点后调用 afterNodeRemoval() 更新双向链表。


五、性能特点

操作时间复杂度说明
插入(putO(1)哈希表插入 + 链表维护
查询(getO(1)哈希表查找 + 链表调整(访问顺序)
删除(removeO(1)哈希表删除 + 链表维护
遍历(forEachO(n)直接遍历双向链表,无需处理哈希桶

六、与 HashMap 的对比

特性HashMapLinkedHashMap
顺序维护无序支持插入顺序或访问顺序
内存占用较低较高(额外存储双向链表指针)
遍历性能需遍历所有桶直接遍历链表,性能更高
适用场景通用键值对存储需要顺序访问或实现 LRU 缓存

总结

LinkedHashMap 通过 哈希表 + 双向链表 的结构,在保留 HashMap 高效查找的基础上,实现了顺序访问特性。其核心设计包括:
双向链表维护顺序:支持插入顺序或访问顺序(LRU)。
灵活的扩展性:通过重写 removeEldestEntry() 实现缓存淘汰策略。
性能平衡:在少量额外内存开销下,提供更可控的遍历顺序。

适用于需要按顺序迭代或实现缓存淘汰机制的场景(如页面置换、资源池管理等)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值