当前位置: 首页 > news >正文

054_TreeMap / LinkedHashMap

一、概述

TreeMap和LinkedHashMap均是Java集合框架中Map接口的重要实现类,在保留Map接口“键值对映射”核心特性的基础上,分别提供了排序能力顺序保留能力,适用于不同的业务场景。

  • TreeMap:基于红黑树实现,支持键的排序(自然排序或定制排序),元素按键的顺序存储和访问。
  • LinkedHashMap:基于哈希表+双向链表实现,保留键值对的插入顺序或访问顺序,兼具哈希表的高效查询和链表的有序性。

在这里插入图片描述

二、TreeMap详解

2.1 底层结构与核心原理

TreeMap底层基于红黑树(一种自平衡二叉搜索树) 实现,其核心原理是通过红黑树的特性保证键的有序性:

  • 红黑树的每个节点存储一个键值对(Entry<K,V>),键的大小关系决定节点在树中的位置。
  • 左子树的所有节点键值小于父节点键值,右子树的所有节点键值大于父节点键值(二叉搜索树特性)。
  • 通过红黑树的自平衡机制(旋转和变色),保证树的高度始终为O(log n),确保增删改查的高效性。

2.2 核心特性

特点说明
有序性元素按键的排序规则存储(非插入顺序),默认升序,可通过比较器定制排序方向。
键唯一性依赖键的比较结果去重:若两键通过compareTo()(或compare())返回0,则视为重复键,新值覆盖旧值。
键可比较性键必须实现Comparable接口(自然排序)或通过构造器传入Comparator(定制排序),否则插入时抛出ClassCastException。
无null键不允许存储null键(排序时无法比较null),但允许null值。
线程不安全非同步实现,多线程并发修改需额外同步(如Collections.synchronizedMap())。
性能增删改查时间复杂度均为O(log n)(红黑树平衡操作开销)。

2.3 排序方式

  1. 自然排序(默认)
    键所属类实现Comparable接口,重写compareTo(T o)方法定义排序规则:
// 自定义键类型(按年龄升序排序)
class User implements Comparable<User> {private String name;private int age;public User(String name, int age) {this.name = name;this.age = age;}// 按年龄升序排序,年龄相同则按姓名排序@Overridepublic int compareTo(User o) {if (this.age != o.age) {return this.age - o.age;}return this.name.compareTo(o.name);}
}
  1. 定制排序(外部比较器)
    创建TreeMap时传入Comparator接口实现类,自定义排序规则(无需键类实现Comparable):
// 定制排序:按User的年龄降序
Comparator<User> ageDescComparator = (u1, u2) -> u2.age - u1.age;
Map<User, String> treeMap = new TreeMap<>(ageDescComparator);

2.4 常用方法与示例

核心方法

方法定义功能说明
V put(K key, V value)插入键值对,按排序规则存储,键重复则覆盖旧值。
V get(Object key)获取指定键对应的值,不存在则返回null。
K firstKey()返回最小的键(按排序规则)。
K lastKey()返回最大的键(按排序规则)。
K ceilingKey(K key)返回大于等于key的最小键,不存在则返回null。
K floorKey(K key)返回小于等于key的最大键,不存在则返回null。
NavigableSet keySet()返回键的有序集合,支持导航操作(如descendingSet()逆序遍历)。

示例代码

import java.util.TreeMap;public class TreeMapDemo {public static void main(String[] args) {// 自然排序:按String的字典序(升序)TreeMap<String, Integer> treeMap = new TreeMap<>();treeMap.put("Banana", 2);treeMap.put("Apple", 1);treeMap.put("Cherry", 3);// 遍历:按键的升序输出System.out.println("键有序遍历:" + treeMap.keySet()); // [Apple, Banana, Cherry]// 导航方法System.out.println("最小键:" + treeMap.firstKey()); // AppleSystem.out.println("大于等于'B'的最小键:" + treeMap.ceilingKey("B")); // BananaSystem.out.println("小于等于'B'的最大键:" + treeMap.floorKey("B")); // Banana}
}

在这里插入图片描述

2.5 适用场景

  • 需要按键排序的场景(如排行榜、字典序查询、区间统计)。
  • 需导航操作的场景(如获取小于/大于指定键的元素、逆序遍历)。
  • 键具有自然排序或可通过比较器定义排序规则的场景。

三、LinkedHashMap详解

3.1 底层结构与核心原理

LinkedHashMap底层基于哈希表(HashMap)+双向链表实现,继承自HashMap,在哈希表的基础上通过双向链表维护键值对的顺序,核心原理是:

  • 哈希表:保证键的唯一性和高效查询(同HashMap,通过hashCode()和equals()去重)。
  • 双向链表:每个节点包含before和after指针,记录节点的前驱和后继,维护键值对的顺序(插入顺序或访问顺序)。

LinkedHashMap通过accessOrder参数(默认false)控制顺序类型:

  • accessOrder = false(默认):按插入顺序排序(插入早的节点在链表前)。
  • accessOrder = true:按访问顺序排序(最近访问的节点移至链表尾部,可实现LRU缓存)。

3.2 核心特性

特点说明
有序性按插入顺序(默认)或访问顺序(accessOrder = true)存储和遍历。
键唯一性同HashMap,依赖hashCode()和equals()保证键的唯一性。
查询性能同哈希表特性,get()和containsKey()时间复杂度为O(1)(无哈希冲突时)。
允许null键/值最多存储一个null键,可存储任意数量的null值。
线程不安全非同步实现,多线程并发修改需额外同步(如Collections.synchronizedMap())。
内存开销比HashMap大(需存储双向链表的before和after指针)。

3.3 常用方法与示例

核心方法(继承自HashMap,新增顺序相关特性):

方法定义功能说明
LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)构造器:指定初始容量、加载因子和顺序类型(accessOrder控制插入/访问顺序)。
void clear()清空所有键值对,重写自HashMap,同时清空双向链表。
Set<Map.Entry<K,V>> entrySet()返回键值对集合,遍历顺序为链表维护的顺序(插入或访问顺序)。

示例1:插入顺序维护

import java.util.LinkedHashMap;
import java.util.Map;public class LinkedHashMapInsertOrderDemo {public static void main(String[] args) {// 默认accessOrder = false:按插入顺序排序Map<String, Integer> linkedMap = new LinkedHashMap<>();linkedMap.put("Apple", 1);linkedMap.put("Banana", 2);linkedMap.put("Cherry", 3);// 遍历:按插入顺序输出System.out.println("插入顺序遍历:");for (Map.Entry<String, Integer> entry : linkedMap.entrySet()) {System.out.println(entry.getKey() + " : " + entry.getValue());}// 输出:Apple : 1 → Banana : 2 → Cherry : 3(与插入顺序一致)}
}

示例2:访问顺序维护(LRU缓存基础)

import java.util.LinkedHashMap;
import java.util.Map;// 按访问顺序排序(最近访问的元素在尾部)
public class LinkedHashMapAccessOrderDemo {public static void main(String[] args) {// accessOrder = true:按访问顺序排序Map<String, Integer> lruMap = new LinkedHashMap<>(16, 0.75f, true);lruMap.put("A", 1);lruMap.put("B", 2);lruMap.put("C", 3);lruMap.get("A"); // 访问"A",触发顺序调整lruMap.get("B"); // 访问"B",触发顺序调整// 遍历:最近访问的元素在尾部System.out.println("访问顺序遍历:");for (Map.Entry<String, Integer> entry : lruMap.entrySet()) {System.out.println(entry.getKey()); // 输出:C → A → B(B最近访问,在最后)}}
}

在这里插入图片描述

3.4 适用场景

  • 需保留插入顺序的场景(如日志记录、历史操作轨迹、配置项映射)。
  • LRU缓存实现(accessOrder = true,最近访问的元素保留,淘汰最久未访问元素)。
  • 频繁遍历且需顺序一致的场景(迭代性能优于HashMap,无需遍历哈希表空桶)。

四、TreeMap与LinkedHashMap对比

维度TreeMapLinkedHashMap
底层结构红黑树哈希表+双向链表
有序性类型键的排序顺序(自然/定制排序)插入顺序或访问顺序
键要求必须可比较(Comparable/Comparator)无特殊要求(支持任意对象)
键唯一性依据compareTo()/compare()返回0hashCode()+equals()
null键支持不允许(抛NullPointerException)允许(最多1个null键)
核心操作性能增删改查O(log n)(红黑树平衡)增删改查O(1)(同HashMap,链表维护开销略高)
迭代性能中(红黑树中序遍历)高(链表顺序迭代,无空桶)
典型应用排序映射、区间查询、导航操作顺序保留、LRU缓存、日志记录

五、使用注意事项与最佳实践

5.1 TreeMap注意事项

  • 键的比较一致性:排序规则(compareTo()/compare())需与equals()逻辑一致(若compareTo()返回0,则equals()应返回true),否则可能出现“逻辑重复键”。
  • 避免频繁修改排序规则:键的比较属性若被修改,可能导致红黑树结构混乱(建议使用不可变对象作为键)。
  • 导航方法利用:充分使用ceilingKey()、floorKey()等导航方法,简化区间查询逻辑。

5.2 LinkedHashMap注意事项

  • 初始容量设置:同HashMap,预估容量减少扩容次数(如new LinkedHashMap<>(100))。
  • LRU缓存实现:重写removeEldestEntry(Map.Entry<K,V> eldest)方法,定义淘汰规则(如超过容量时删除最久未访问元素):
    // LRU缓存示例(容量为3)
    Map<String, Integer> lruCache = new LinkedHashMap<>(3, 0.75f, true) {@Overrideprotected boolean removeEldestEntry(Map.Entry<String, Integer> eldest) {return size() > 3; // 容量超过3时,删除最久未访问元素}
    };
    
  • 线程安全:多线程环境需通过Collections.synchronizedMap()包装,或使用ConcurrentHashMap(无顺序需求时)。

六、总结

TreeMap和LinkedHashMap作为Map接口的重要实现,分别聚焦“排序能力”和“顺序保留能力”:

  • TreeMap:通过红黑树实现键的排序,适用于需按键排序、区间查询或导航操作的场景,核心优势是有序性和导航方法。
  • LinkedHashMap:通过哈希表+双向链表保留顺序,适用于需插入/访问顺序一致或LRU缓存的场景,核心优势是顺序可控和高效迭代。

选择时需根据“是否需要排序”

http://www.dtcms.com/a/292742.html

相关文章:

  • 小程序上传头像解析
  • numpy库 降维,矩阵创建与元素的选取,修改
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘Cython’问题
  • Protobuf学习
  • SDC命令详解:使用set_min_library命令进行约束
  • fuse低代码工作流平台概述【已开源】-自研
  • AWS: 云上侦探手册,七步排查ALB与EC2连接疑云
  • Kotlin调试
  • PyQt5在Pycharm上的环境搭建 -- Qt Designer + Pyuic + Pyrcc组合,大幅提升GUI开发效率
  • 测试学习之——requests day01
  • 【数据结构初阶】--栈和队列(一)
  • 注意力机制介绍
  • 从链式协同到生态共生:制造业数智化供应链跃升之路
  • spring boot 项目如何使用jasypt加密
  • 【中文翻译】SmolVLA:面向低成本高效机器人的视觉-语言-动作模型
  • 认识自我的机器人:麻省理工学院基于视觉的系统让机器了解自身机体
  • 机器人芯片(腾讯元宝)
  • 《小白学习产品经理》第八章:方法论之马斯洛需求层次理论
  • 【JS】获取元素宽高(例如div)
  • 暑假算法训练.6
  • 单片机学习笔记.单总线one-wire协议(这里以普中开发板DS18B20为例)
  • SQL JOIN 全解析:用 `users` 与 `orders` 表彻底掌握内连接、左连接、右连接
  • PostgreSQL大数据集查询优化
  • 蓝桥杯51单片机
  • 第十四届蓝桥杯青少Scratch国赛真题——太空大战
  • 解决 NCCL 多节点通信问题:从 nranks 1 到 busbw 116 MB/s
  • 02-netty基础-java四种IO模型
  • 二、计算机网络技术——第3章:数据链路层
  • Yocto meta-toradex-security layer 使用 TI AM62 安全启动功能
  • vscode,cursor,Trae终端不能使用cnpm、npm、pnpm命令解决方案