java TreeMap 详解
Java TreeMap 详解
TreeMap
是 Java 中的一个基于红黑树实现的 NavigableMap
,它提供了键值对的按键自然顺序(或用户自定义顺序)排序的映射表。它是 Map
接口的一个实现类,属于 java.util
包。
1. TreeMap 的特点
-
有序性
TreeMap
按键的自然顺序 (Comparable
) 或自定义顺序 (Comparator
) 排序。- 默认按键的升序排列。
-
基于红黑树实现
TreeMap
底层是红黑树,保证插入、删除、查询操作的时间复杂度为 O(log n)。
-
不允许键为
null
- 键必须实现
Comparable
或提供Comparator
,否则会抛出NullPointerException
。 - 值可以为
null
。
- 键必须实现
-
线程不安全
TreeMap
是非同步的,如果需要线程安全,可以使用Collections.synchronizedSortedMap()
方法。
-
实现接口
NavigableMap
SortedMap
Map
Cloneable
Serializable
2. TreeMap 的常用构造方法
TreeMap<K, V> map = new TreeMap<>();
构造方法 | 描述 |
---|---|
TreeMap() | 创建一个空的 TreeMap ,按键的自然顺序排序。 |
TreeMap(Comparator<? super K> comparator) | 创建一个空的 TreeMap ,按指定的比较器排序。 |
TreeMap(Map<? extends K, ? extends V> m) | 用指定的 Map 初始化 TreeMap 。 |
TreeMap(SortedMap<K, ? extends V> sm) | 用指定的 SortedMap 初始化 TreeMap 。 |
3. TreeMap 的常用方法
方法名称 | 描述 |
---|---|
put(K key, V value) | 添加键值对。如果键已存在,则更新对应的值。 |
get(Object key) | 根据键获取对应的值。 |
remove(Object key) | 删除指定键的键值对。 |
containsKey(Object key) | 检查是否包含指定的键。 |
containsValue(Object value) | 检查是否包含指定的值。 |
size() | 返回键值对数量。 |
isEmpty() | 检查映射是否为空。 |
firstKey() | 返回最小的键(按排序规则)。 |
lastKey() | 返回最大的键(按排序规则)。 |
headMap(K toKey) | 返回小于指定键的子映射。 |
tailMap(K fromKey) | 返回大于或等于指定键的子映射。 |
subMap(K fromKey, K toKey) | 返回指定范围内的子映射。 |
keySet() | 返回所有键的集合(按排序规则)。 |
values() | 返回所有值的集合。 |
entrySet() | 返回所有键值对的集合(按排序规则)。 |
clear() | 清空所有键值对。 |
floorKey(K key) | 返回小于或等于指定键的最大键。 |
ceilingKey(K key) | 返回大于或等于指定键的最小键。 |
higherKey(K key) | 返回大于指定键的最小键。 |
lowerKey(K key) | 返回小于指定键的最大键。 |
pollFirstEntry() | 移除并返回最小键的键值对。 |
pollLastEntry() | 移除并返回最大键的键值对。 |
4. TreeMap 使用示例
基本操作
import java.util.*;
public class TreeMapExample {
public static void main(String[] args) {
// 创建 TreeMap
TreeMap<Integer, String> map = new TreeMap<>();
// 添加键值对
map.put(3, "Three");
map.put(1, "One");
map.put(2, "Two");
map.put(5, "Five");
map.put(4, "Four");
// 按照键的自然顺序输出
System.out.println("TreeMap: " + map);
// 获取元素
System.out.println("Key 3 maps to: " + map.get(3));
// 删除元素
map.remove(2);
System.out.println("After removing key 2: " + map);
// 遍历键值对
System.out.println("Keys:");
for (Integer key : map.keySet()) {
System.out.println(key + " -> " + map.get(key));
}
// 获取子映射
System.out.println("HeadMap (keys < 4): " + map.headMap(4));
System.out.println("TailMap (keys >= 4): " + map.tailMap(4));
System.out.println("SubMap (2 <= keys < 5): " + map.subMap(2, 5));
}
}
输出:
TreeMap: {1=One, 2=Two, 3=Three, 4=Four, 5=Five}
Key 3 maps to: Three
After removing key 2: {1=One, 3=Three, 4=Four, 5=Five}
Keys:
1 -> One
3 -> Three
4 -> Four
5 -> Five
HeadMap (keys < 4): {1=One, 3=Three}
TailMap (keys >= 4): {4=Four, 5=Five}
SubMap (2 <= keys < 5): {3=Three, 4=Four}
自定义排序
通过 Comparator
自定义键的排序规则。
import java.util.*;
public class TreeMapCustomSort {
public static void main(String[] args) {
// 按键的降序排序
TreeMap<Integer, String> map = new TreeMap<>(Comparator.reverseOrder());
// 添加键值对
map.put(3, "Three");
map.put(1, "One");
map.put(2, "Two");
map.put(5, "Five");
map.put(4, "Four");
// 输出 TreeMap
System.out.println("TreeMap with custom order: " + map);
}
}
输出:
TreeMap with custom order: {5=Five, 4=Four, 3=Three, 2=Two, 1=One}
NavigableMap 的操作
TreeMap
实现了 NavigableMap
,支持范围查询、边界查询等高级功能。
import java.util.*;
public class TreeMapNavigable {
public static void main(String[] args) {
TreeMap<Integer, String> map = new TreeMap<>();
map.put(10, "Ten");
map.put(20, "Twenty");
map.put(30, "Thirty");
map.put(40, "Forty");
// 边界查询
System.out.println("Lower than 30: " + map.lowerKey(30));
System.out.println("Floor of 30: " + map.floorKey(30));
System.out.println("Higher than 30: " + map.higherKey(30));
System.out.println("Ceiling of 30: " + map.ceilingKey(30));
// 获取并移除最小和最大元素
System.out.println("Poll first entry: " + map.pollFirstEntry());
System.out.println("Poll last entry: " + map.pollLastEntry());
// 输出最终的 TreeMap
System.out.println("Remaining TreeMap: " + map);
}
}
输出:
Lower than 30: 20
Floor of 30: 30
Higher than 30: 40
Ceiling of 30: 30
Poll first entry: 10=Ten
Poll last entry: 40=Forty
Remaining TreeMap: {20=Twenty, 30=Thirty}
线程安全的 TreeMap
如果需要线程安全的 TreeMap
,可以使用以下方法:
Map<Integer, String> synchronizedMap = Collections.synchronizedMap(new TreeMap<>());
5. TreeMap 与 HashMap 对比
特性 | TreeMap | HashMap |
---|---|---|
实现方式 | 基于红黑树实现 | 基于哈希表实现 |
键值对顺序 | 按键的自然顺序或自定义顺序排列 | 无序 |
时间复杂度 | 插入、删除、查找的时间复杂度为 O ( log n ) O(\log n) O(logn) | 插入、删除、查找的时间复杂度为 O ( 1 ) O(1) O(1)(理想情况) |
键是否允许为 null | 不允许键为 null | 允许一个键为 null |
性能 | 较慢,因为需要维护排序 | 较快,适用于不需要顺序的场景 |
适用场景 | 需要排序、范围查询时 | 不需要顺序、快速插入和查询时 |
6. TreeMap 的应用场景
1. 排序数据
- 需要对键按自然顺序或自定义规则排序时使用。
- 示例:学生成绩单按学号或分数排序。
2. 范围查询
TreeMap
提供headMap
、tailMap
和subMap
方法,便于实现范围查询。- 示例:获取某个范围内的日期或价格区间的值。
3. 边界操作
- 支持
higherKey
、lowerKey
、ceilingKey
和floorKey
方法,适合边界查询。 - 示例:实现一个简易的日程安排系统,快速查询最近的空闲时间段。
4. 有序数据统计
- 红黑树底层实现,适合需要动态更新并保持有序的数据。
- 示例:股票交易记录,随时按时间戳查询最低或最高价格。
7. 常见问题
1. 为什么 TreeMap 的键不能为 null
?
- 因为
TreeMap
的键需要进行排序(依赖compareTo
方法或Comparator
),而null
无法参与比较操作,会抛出NullPointerException
。
2. TreeMap 和 LinkedHashMap 有什么区别?
TreeMap
按键的自然顺序或自定义顺序排序。LinkedHashMap
按插入顺序或访问顺序排列键值对。
8. TreeMap 的底层原理
1. 红黑树结构
TreeMap
使用红黑树实现。- 红黑树是一种自平衡二叉搜索树,保证树的高度为 O ( log n ) O(\log n) O(logn)。
- 节点的插入、删除、查询均保持 O ( log n ) O(\log n) O(logn) 的时间复杂度。
2. 操作过程
- 插入:新键插入到树中后,红黑树可能失衡,会通过旋转和颜色调整重新平衡。
- 删除:删除节点后,红黑树通过调整保持平衡。
- 查找:通过键值的比较递归查找对应节点。
9. 总结
优势
- 键值对按键排序,支持范围查询、边界操作。
- 高效:插入、删除、查找操作的时间复杂度为 O ( log n ) O(\log n) O(logn)。
- 灵活:支持自定义比较规则,满足复杂排序需求。
劣势
- 键不允许为
null
。 - 与
HashMap
相比,插入、查询性能较慢。 - 线程不安全,需要额外同步处理。
适用场景
- 数据需要按键排序。
- 需要快速实现范围查询或边界查询。
- 动态数据的统计和更新。
如果数据不需要排序,可以优先选择 HashMap
,它的性能更优。