介绍
TreeMap 是 Java 中基于 红黑树(自平衡二叉查找树) 实现的 Map 接口实现类,其核心特点是键(key)有序,支持高效的插入、查询和删除操作(时间复杂度均为 O(log n))。以下是 TreeMap 的详细用法:
一、基本特性
- 键的有序性:默认按键的自然排序(实现
Comparable接口),或通过Comparator自定义排序规则。 - 无重复键:键唯一,若插入重复键,新值会覆盖旧值。
- 非同步:线程不安全,多线程环境需手动同步(如使用
Collections.synchronizedMap())。 - null 键处理:
- 自然排序时,键不能为
null(会抛出NullPointerException)。 - 自定义
Comparator时,若支持null键则可插入(需在比较器中处理null)。
- 自然排序时,键不能为
二、初始化与基本操作
1. 初始化
import java.util.TreeMap;
import java.util.Comparator;
public class TreeMapDemo {
public static void main(String[] args) {
// 1. 默认构造:按键的自然排序(键需实现 Comparable)
TreeMap<Integer, String> treeMap1 = new TreeMap<>();
// 2. 自定义排序:通过 Comparator 实现(例:整数降序)
TreeMap<Integer, String> treeMap2 = new TreeMap<>(Comparator.reverseOrder());
// 3. 基于已有的 Map 初始化
TreeMap<Integer, String> treeMap3 = new TreeMap<>(treeMap1);
}
}
2. 常用方法(CRUD)
TreeMap<Integer, String> map = new TreeMap<>();
// 添加/修改键值对(key 存在则覆盖值)
map.put(3, "C");
map.put(1, "A");
map.put(2, "B");
map.put(3, "C-Update"); // 覆盖 key=3 的值
// 获取值
String value = map.get(2); // 返回 "B"
// 移除键值对
map.remove(1); // 移除 key=1 的条目
// 判断键是否存在
boolean hasKey = map.containsKey(2); // true
// 判断值是否存在
boolean hasValue = map.containsValue("B"); // true
// 获取键的集合(有序)
System.out.println(map.keySet()); // [2, 3]
// 获取值的集合(无序,因值可重复)
System.out.println(map.values()); // [B, C-Update]
// 获取键值对集合(有序)
System.out.println(map.entrySet()); // [2=B, 3=C-Update]
三、有序性相关方法(核心特性)
TreeMap 提供了一系列基于键有序性的特殊方法,方便范围查询和边界操作:
| 方法 | 说明 | 示例(map 键为 2,3,5,7) |
|---|---|---|
firstKey() |
返回最小的键 | map.firstKey() → 2 |
lastKey() |
返回最大的键 | map.lastKey() → 7 |
lowerKey(k) |
返回小于 k 的最大键 |
map.lowerKey(5) → 3 |
higherKey(k) |
返回大于 k 的最小键 |
map.higherKey(5) → 7 |
floorKey(k) |
返回小于等于 k 的最大键 |
map.floorKey(6) → 5 |
ceilingKey(k) |
返回大于等于 k 的最小键 |
map.ceilingKey(4) → 5 |
headMap(k) |
返回键小于 k 的子映射(视图) |
map.headMap(5) → {2=B, 3=C} |
tailMap(k) |
返回键大于等于 k 的子映射(视图) |
map.tailMap(5) → {5=E, 7=G} |
subMap(k1, k2) |
返回键大于等于 k1 且小于 k2 的子映射 |
map.subMap(3,7) → {3=C,5=E} |
示例:范围查询
TreeMap<Integer, String> map = new TreeMap<>();
map.put(2, "B");
map.put(3, "C");
map.put(5, "E");
map.put(7, "G");
// 获取所有键小于 5 的键值对
System.out.println(map.headMap(5)); // {2=B, 3=C}
// 获取键在 [3,7) 之间的键值对
System.out.println(map.subMap(3, 7)); // {3=C, 5=E}
// 获取大于等于 5 的最小键
System.out.println(map.ceilingKey(5)); // 5
四、自定义排序(Comparator)
当键未实现 Comparable,或需要自定义排序规则时,可通过 Comparator 实现。
示例:按字符串长度排序
// 键为 String,按字符串长度升序排序(长度相同则按字典序)
TreeMap<String, Integer> map = new TreeMap<>(
Comparator.comparingInt(String::length)
.thenComparing(Comparator.naturalOrder()) // 长度相同则按自然顺序
);
map.put("apple", 5); // 长度 5
map.put("banana", 6); // 长度 6
map.put("pear", 4); // 长度 4
map.put("orange", 6); // 长度 6(与 banana 长度相同)
// 遍历:按长度升序,长度相同则按字典序
for (var entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// 输出:
// pear: 4
// apple: 5
// banana: 6
// orange: 6
五、遍历方式
TreeMap 的遍历结果按键的排序规则有序输出,常用遍历方式:
遍历键值对(
entrySet())for (var entry : map.entrySet()) { System.out.println(entry.getKey() + " → " + entry.getValue()); }遍历键(
keySet())for (String key : map.keySet()) { System.out.println(key + " → " + map.get(key)); }迭代器遍历(支持删除)
var iterator = map.entrySet().iterator(); while (iterator.hasNext()) { var entry = iterator.next(); if (entry.getValue() < 5) { iterator.remove(); // 安全删除 } }
六、注意事项
排序一致性:
equals()与compareTo()/compare()需保持一致(即若a.equals(b)为true,则compare(a,b)必须返回 0),否则可能导致逻辑错误(如containsKey()结果不符合预期)。性能对比:
- 与
HashMap相比,TreeMap插入/查询效率略低(O(log n)vsO(1)),但支持有序操作。 - 与
LinkedHashMap相比,TreeMap的有序性是按键排序,而LinkedHashMap是按插入顺序。
- 与
线程安全:多线程环境下,可通过
Collections.synchronizedMap(new TreeMap<>())包装为同步集合。
总结
TreeMap 适合需要键有序的场景(如字典排序、范围统计、排行榜等),其核心优势在于基于红黑树的高效有序操作。使用时需注意键的排序规则(自然排序或自定义 Comparator),并合理利用范围查询方法提升效率。