HashMap为什么要拼上LinkedList ,从设计思想、应用场景来分析

124 阅读5分钟

LinkedHashMap 是 Java 中的一种实现了 Map 接口的集合类,它结合了 哈希表(Hash Table)双向链表(Doubly Linked List) 的优点。LinkedHashMap 继承自 HashMap,但比 HashMap 多了一个双向链表来维护元素的插入顺序或访问顺序。这个特性使得 LinkedHashMap 在一些特定场景下具有独特的优势。

1. 数据结构与设计思想

哈希表 + 双向链表

LinkedHashMap 的核心数据结构是 哈希表双向链表 的结合:

  • 哈希表(Hash Table) :类似于 HashMapLinkedHashMap 使用哈希表来存储数据,提供快速的查找、插入和删除操作。它通过一个哈希函数将每个键值对映射到哈希表的一个桶中,确保常数时间复杂度(O(1))的查找和插入。

  • 双向链表(Doubly Linked List) :与 HashMap 只用哈希表不同,LinkedHashMap 还维护了一个双向链表。链表中的每个节点包含一个键值对,同时每个节点都有指向前后节点的指针。这个双向链表可以让 LinkedHashMap 保持元素的插入顺序或访问顺序。

    这种结构的优势是,可以在常数时间内(O(1))实现元素的访问和插入,同时还能按插入顺序或者访问顺序遍历元素。

维护顺序

LinkedHashMap 提供了两种顺序维护方式:

  1. 插入顺序(Insertion Order) :如果不设置特殊的访问顺序,LinkedHashMap 会按照元素被插入的顺序进行维护。也就是说,插入的第一个元素排在前面,最后插入的元素排在最后。
  2. 访问顺序(Access Order) :如果在构造 LinkedHashMap 时设置 accessOrder = true,则元素的顺序会根据它们的访问顺序进行调整。每次访问(get 或者 put)一个元素时,它会被移动到链表的尾部,变成最新访问的元素。这种方式通常用于实现 LRU(Least Recently Used,最近最少使用)缓存。

2. 应用场景

1. 保持元素插入顺序

LinkedHashMap 在需要按照元素插入顺序遍历数据时非常有用。它能够在哈希表提供快速查找和更新的同时,还能保留元素的插入顺序。

  • 常见场景

    • 需要维护插入顺序的缓存:如果你需要在缓存中保持数据的插入顺序,例如,按照用户提交数据的顺序返回结果时,LinkedHashMap 是一个理想选择。
    • 数据序列化和输出:当数据需要按插入顺序输出时,例如,JSON 序列化或数据库查询结果的输出,LinkedHashMap 可以非常方便地做到这一点。

2. 实现 LRU 缓存

LinkedHashMap 的访问顺序维护特性使它成为实现 LRU(Least Recently Used)缓存 的理想数据结构。LRU 缓存要求移除最久未被访问的元素,而 LinkedHashMap 提供的 accessOrder 选项允许你在每次访问时将元素移到链表尾部,因此,可以通过检查链表头部的元素来高效地删除最不常用的数据。

  • 常见场景

    • LRU 缓存实现:比如操作系统中的页面缓存、Web 应用的会话缓存等。通过 LinkedHashMap,我们可以通过设置最大缓存大小并重写 removeEldestEntry 方法来自动删除最不常用的数据。

    代码示例:

    int MAX_ENTRIES = 100;
    LinkedHashMap<Integer, String> cache = new LinkedHashMap<>(16, 0.75f, true) {
        @Override
        protected boolean removeEldestEntry(Map.Entry<Integer, String> eldest) {
            return size() > MAX_ENTRIES;
        }
    };
    

    这种方式非常适合用于内存缓存或其他需要快速淘汰老旧数据的场景。

3. 需要快速查找同时保持顺序的场景

LinkedHashMap 可以用来实现需要在保证查找效率的同时,还需保持数据顺序的应用。例如,处理历史记录或访问日志时,既要快速查找某条记录,又要按时间顺序或访问顺序输出数据。

  • 常见场景

    • 历史记录存储:例如,浏览器的历史记录,或者文件系统的最近访问文件。
    • 按顺序处理数据:例如,在任务调度或事件驱动的系统中,根据任务的到达顺序来执行处理。

4. 数据排序与重排

LinkedHashMap 在需要按访问顺序或插入顺序对数据进行排序时非常有用。通过访问顺序(accessOrder = true)和链表特性,LinkedHashMap 使得按最近使用顺序对数据进行重排序变得非常简单。

  • 常见场景

    • 按最近使用顺序进行排序:例如,在实现一个最近访问的活动列表时,可以根据访问顺序对活动进行排序,使最近访问的活动排在前面。
    • 按访问频率排序:这种场景可以结合访问顺序与频率来优化数据处理。

3. 总结

LinkedHashMap 是一个结合了哈希表和双向链表的数据结构。它不仅能够提供快速的查找和插入性能(通过哈希表),还能够在此基础上保持元素的插入顺序或访问顺序(通过双向链表)。因此,LinkedHashMap 非常适合以下场景:

  • 需要保持元素顺序的缓存和存储。
  • 实现 LRU 缓存(通过访问顺序)。
  • 需要按顺序输出数据的应用场景。
  • 需要快速查找同时保持顺序的应用场景。