讲师中心 微信公众号
AI工具推荐 视频效率加速

如何在Java中使用LinkedHashMap

落芳同学_4698

落芳同学_4698

发布时间:2025-09-20 09:26:01

|

613人浏览过

|

来源于php中文网

原创

LinkedHashMap通过哈希表和双向链表结合,既保证O(1)操作性能,又维护插入或访问顺序,适用于需顺序迭代或实现LRU缓存的场景。

如何在java中使用linkedhashmap

在Java中,

LinkedHashMap
是一个非常实用的集合类,它继承自
HashMap
,但在此基础上增加了一个关键特性:它能记住元素被插入的顺序(或访问的顺序)。这意味着当你遍历
LinkedHashMap
时,元素的顺序会和你当初放入时的顺序保持一致,这对于很多需要保持数据序列的场景来说,简直是量身定制。它本质上是
HashMap
和双向链表的结合体,既提供了哈希表的快速查找能力,又通过链表维护了元素的顺序。

解决方案

使用

LinkedHashMap
其实和使用
HashMap
大同小异,核心API都是那些。

首先,你需要实例化它:

import java.util.LinkedHashMap;
import java.util.Map;

public class LinkedHashMapDemo {
    public static void main(String[] args) {
        // 创建一个LinkedHashMap,默认是按插入顺序
        LinkedHashMap<String, Integer> userScores = new LinkedHashMap<>();

        // 添加元素,就像HashMap一样
        userScores.put("Alice", 95);
        userScores.put("Bob", 88);
        userScores.put("Charlie", 92);
        userScores.put("David", 76);

        System.out.println("按插入顺序遍历:");
        for (Map.Entry<String, Integer> entry : userScores.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }

        // 访问一个元素,如果LinkedHashMap是以访问顺序排序的,这个元素会被移到链表末尾
        userScores.get("Bob");
        System.out.println("\n访问Bob后,再次遍历(如果accessOrder为true,Bob会移到末尾):");
        for (Map.Entry<String, Integer> entry : userScores.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }

        // 移除元素
        userScores.remove("Charlie");
        System.out.println("\n移除Charlie后:");
        for (Map.Entry<String, Integer> entry : userScores.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
    }
}

你会发现,上面的代码中,第一次遍历时输出的顺序是Alice, Bob, Charlie, David。即便我们访问了"Bob",在默认的插入顺序模式下,它的位置并不会改变。但如果我们在构造

LinkedHashMap
时传入
true
作为
accessOrder
参数,那么访问操作就会影响元素的顺序,被访问的元素会被移到链表的末尾,这对于实现LRU(最近最少使用)缓存非常有用。

立即学习“Java免费学习笔记(深入)”;

LinkedHashMap与HashMap、TreeMap有什么区别?何时选择LinkedHashMap?

这真的是个好问题,因为它们都是

Map
接口的实现,但内部机制和适用场景却截然不同。在我看来,理解它们之间的差异是选择正确工具的关键。

  • HashMap
    : 这是最常用的
    Map
    实现,它不保证任何顺序。元素存储的顺序完全取决于哈希码和内部的哈希表结构。它的优势在于性能极高,对于
    put
    、
    get
    、
    remove
    等基本操作,平均时间复杂度是O(1)。如果你只关心快速存取,对元素的顺序没有任何要求,那
    HashMap
    就是你的首选。
  • TreeMap
    :
    TreeMap
    实现了
    SortedMap
    接口,它会根据键的自然顺序(或者你提供的
    Comparator
    )对元素进行排序。这意味着当你遍历
    TreeMap
    时,元素总是按键的升序(或自定义顺序)排列。它的操作时间复杂度是O(logN),因为它的底层是红黑树。当你需要一个始终保持有序的映射时,
    TreeMap
    是最佳选择。
  • LinkedHashMap
    : 就像前面说的,它在
    HashMap
    的基础上增加了顺序保证。默认情况下是按插入顺序,也可以配置为按访问顺序。它的操作性能与
    HashMap
    相近,基本也是O(1),但由于需要维护双向链表,会比纯粹的
    HashMap
    略微消耗更多内存。

何时选择

LinkedHashMap
?

  1. 需要保持插入顺序的场景:比如你有一个配置列表,你希望配置项的显示顺序和你在文件中定义的顺序一致;或者一个任务队列,任务的执行顺序应该和添加顺序一致。
  2. 实现LRU(最近最少使用)缓存:这是
    LinkedHashMap
    一个非常经典的用途,利用它的访问顺序特性可以高效地实现LRU策略。
  3. 迭代顺序很重要但又不想牺牲太多性能:如果
    TreeMap
    的O(logN)性能开销在你看来有点大,而你又需要一个可预测的迭代顺序,那么
    LinkedHashMap
    的O(1)平均性能加上顺序保证,就显得非常平衡了。

总的来说,如果你对顺序有要求,但又不需要键的自然排序,那么

LinkedHashMap
通常会是比
TreeMap
更轻量且性能更好的选择。

如何利用LinkedHashMap实现LRU缓存机制?

用

LinkedHashMap
实现LRU缓存,这简直是教科书般的用法。它的一个构造函数允许你指定
accessOrder
参数,如果设置为
true
,那么每次对
Map
中的元素进行
get
或
put
操作时,该元素都会被移动到链表的末尾,这样链表头部就是最久未使用的元素。

更巧妙的是,

LinkedHashMap
提供了一个受保护的方法
removeEldestEntry(Map.Entry<K,V> eldest)
。你可以重写这个方法,来定义何时移除最老的(也就是链表头部的)元素。

下面是一个简单的LRU缓存实现示例:

java-api-doc-generator
java-api-doc-generator

分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。

下载
import java.util.LinkedHashMap;
import java.util.Map;

public class LRUCache<K, V> extends LinkedHashMap<K, V> {
    private final int capacity;

    public LRUCache(int capacity) {
        // 调用LinkedHashMap的构造函数
        // initialCapacity: 初始容量
        // loadFactor: 负载因子
        // accessOrder: true表示按访问顺序排序,get或put会把元素移到链表末尾
        super(capacity, 0.75f, true);
        this.capacity = capacity;
    }

    // 重写这个方法来判断何时移除最老的条目
    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        // 当Map的大小超过容量时,返回true,LinkedHashMap会自动移除最老的条目
        return size() > capacity;
    }

    public static void main(String[] args) {
        LRUCache<String, Integer> cache = new LRUCache<>(3); // 容量为3

        cache.put("A", 1); // A
        cache.put("B", 2); // A, B
        cache.put("C", 3); // A, B, C

        System.out.println("初始缓存: " + cache); // {A=1, B=2, C=3}

        cache.get("A"); // 访问A,A变为最新 {B=2, C=3, A=1}
        System.out.println("访问A后: " + cache);

        cache.put("D", 4); // 添加D,超出容量,最老的B被移除 {C=3, A=1, D=4}
        System.out.println("添加D后: " + cache);

        cache.put("E", 5); // 添加E,超出容量,最老的C被移除 {A=1, D=4, E=5}
        System.out.println("添加E后: " + cache);
    }
}

这段代码清晰地展示了LRU缓存的核心逻辑。通过

accessOrder = true
,每次访问(
get
或
put
)都会把元素提到“最新”的位置;通过重写
removeEldestEntry
,我们定义了缓存满时移除“最老”元素的策略。这简直是优雅和高效的完美结合。

LinkedHashMap的内部实现原理是什么?它如何保证插入顺序或访问顺序?

要理解

LinkedHashMap
的精髓,就得稍微深入它的内部实现。我个人觉得,理解这些底层机制,能帮助我们更好地预判和解决问题。

LinkedHashMap
的核心在于它巧妙地结合了两种数据结构:

  1. 哈希表(Hash Table):这部分和
    HashMap
    是一样的,它负责通过键的哈希值快速定位到对应的键值对(
    Entry
    对象)。哈希表提供了O(1)的平均查找、插入和删除性能。
  2. 双向链表(Doubly Linked List):这是
    LinkedHashMap
    与众不同之处。每一个
    Entry
    对象除了包含键、值和指向哈希表中下一个元素的指针外,还额外包含了指向链表中前一个元素(
    before
    )和后一个元素(
    after
    )的指针。这个双向链表将所有键值对按照它们被插入(或访问)的顺序连接起来。

如何保证插入顺序?

当

accessOrder
设置为
false
(默认值)时,
LinkedHashMap
维护的是插入顺序。

  • 插入(
    put
    )操作
    :
    • 如果键是新的,
      LinkedHashMap
      会在哈希表中找到或创建一个新的
      Entry
      。同时,这个新的
      Entry
      会被添加到双向链表的末尾。
    • 如果键已经存在,它会更新对应的值,但该
      Entry
      在链表中的位置保持不变。
  • 删除(
    remove
    )操作
    :
    • 从哈希表中移除
      Entry
      。
    • 同时,从双向链表中移除对应的
      Entry
      ,通过调整其前后元素的
      before
      和
      after
      指针来“跳过”它。

所以,当遍历

LinkedHashMap
时,它会沿着这个双向链表从头到尾遍历,自然就得到了插入时的顺序。

如何保证访问顺序?

当

accessOrder
设置为
true
时,
LinkedHashMap
维护的是访问顺序。

  • 插入(
    put
    )操作
    :
    • 如果键是新的,行为和插入顺序模式一样,新
      Entry
      添加到链表末尾。
    • 如果键已经存在,更新值后,对应的
      Entry
      会从它当前在链表中的位置移除,然后重新添加到链表的末尾。
  • 访问(
    get
    )操作
    :
    • 当通过
      get
      方法访问一个
      Entry
      时,如果
      accessOrder
      为
      true
      ,这个
      Entry
      也会从它当前在链表中的位置移除,然后重新添加到链表的末尾。

这种“被访问就移到末尾”的机制,使得链表头部总是那些最久未被访问的元素,而链表末尾则是最近被访问的元素。这正是LRU缓存所需要的。

性能考量:

相比于纯粹的

HashMap
,
LinkedHashMap
由于需要额外维护双向链表,每个
Entry
会多存储两个指针(
before
和
after
),因此会占用更多的内存空间。同时,链表的维护操作(如移动节点)也会带来微小的额外开销。但在大多数情况下,这些开销对于
LinkedHashMap
所带来的顺序保证特性来说,是完全值得的,并且其平均O(1)的性能依然非常优秀。

热门AI工具

更多
WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

二狗PPT
二狗PPT Hot

一款AI演示文稿工具,主要用于专为中式职场打造的AI PPT生成工具,适合需要提升相关任务效率的用户。

PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

AionClaw
AionClaw Hot

AionClaw是一款面向办公、创作和编程任务的AI桌面智能体。

咔片AIPPT

一款在线AI演示文稿制作工具,可根据主题和内容需求辅助生成PPT结构与页面,提高演示材料制作效率。

UP简历
UP简历 Hot

一款AI办公效率工具,主要用于基于AI技术的免费在线简历制作工具,适合需要提升相关任务效率的用户。

墨刀AI
墨刀AI Hot

一款AI图像与设计工具,主要用于产品经理的专属智能体,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

相关专题

更多
C++ 智能指针与现代内存管理
C++ 智能指针与现代内存管理

深入讲解 C++ 现代内存管理的核心工具——智能指针,涵盖 unique_ptr 独占所有权语义、shared_ptr 引用计数机制与循环引用问题、weak_ptr 弱引用的应用场景、make_unique/make_shared 工厂函数的性能优势、自定义删除器的编写、RAII 资源管理思想的实践,以及从裸指针迁移到智能指针的重构策略,帮助开发者编写安全无泄漏的现代 C++ 代码。

379

2026.04.23

treenode的用法
treenode的用法

​在计算机编程领域,TreeNode是一种常见的数据结构,通常用于构建树形结构。在不同的编程语言中,TreeNode可能有不同的实现方式和用法,通常用于表示树的节点信息。更多关于treenode相关问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2661

2023.12.01

C++ 高效算法与数据结构
C++ 高效算法与数据结构

本专题讲解 C++ 中常用算法与数据结构的实现与优化,涵盖排序算法(快速排序、归并排序)、查找算法、图算法、动态规划、贪心算法等,并结合实际案例分析如何选择最优算法来提高程序效率。通过深入理解数据结构(链表、树、堆、哈希表等),帮助开发者提升 在复杂应用中的算法设计与性能优化能力。

376

2025.12.22

深入理解算法:高效算法与数据结构专题
深入理解算法:高效算法与数据结构专题

本专题专注于算法与数据结构的核心概念,适合想深入理解并提升编程能力的开发者。专题内容包括常见数据结构的实现与应用,如数组、链表、栈、队列、哈希表、树、图等;以及高效的排序算法、搜索算法、动态规划等经典算法。通过详细的讲解与复杂度分析,帮助开发者不仅能熟练运用这些基础知识,还能在实际编程中优化性能,提高代码的执行效率。本专题适合准备面试的开发者,也适合希望提高算法思维的编程爱好者。

417

2026.01.06

C++ 数据结构与算法实现教程合集
C++ 数据结构与算法实现教程合集

以 C++ 为实现语言,系统讲解核心数据结构与算法,涵盖链表(单链表/双链表/环检测)、栈与队列(单调栈/优先队列)、二叉树(遍历/BST/AVL/红黑树)、哈希表(开地址法/链地址法)、图(邻接表/BFS/DFS/Dijkstra/拓扑排序)、常见排序算法(快排/归并/堆排/计数排序)的实现与复杂度分析,同时分享 LeetCode 刷题技巧、竞赛编程常用模板(二分/前缀和/滑动窗口/动态规划),帮助开发者夯实算法基础。

472

2026.05.09

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

3188

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

5089

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

3829

2025.12.29

Vite 项目如何打包和部署
Vite 项目如何打包和部署

Vite项目打包部署专题汇集生产构建、产物预览、输出目录、资源路径、Nginx与GitHub Pages部署及常见白屏和路由404问题的已审核文章,帮助读者完成上线流程。

20

2026.10.28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
dev.java 官方:Learn Java
dev.java 官方:Learn Java

共0课时 | 0人学习

Java JDBC数据库连接官方教程
Java JDBC数据库连接官方教程

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn