headmap 和 tailmap 是 treemap 的视图方法,返回共享底层红黑树的子映射,非独立副本;修改任一视图会实时影响原 map,且存在边界包含性不对称、gc 风险与性能开销等陷阱。

headMap 和 tailMap 是什么,为什么不能直接 new 子 Map
headMap 和 tailMap 不是复制数据,而是返回原 TreeMap 的「视图」——底层共享同一棵红黑树,修改子视图会实时反映到原 Map,反之亦然。这是性能关键:避免 O(n) 拷贝,但也是最常踩的坑。
- 误以为
headMap("C")返回的是独立副本,结果在子视图里put,原 Map 也多了一条记录 - 想“只读”用,却没加防御性包装,导致业务逻辑意外污染上游数据
- 用完子视图后仍长期持有引用,阻碍原
TreeMap被 GC(尤其大 Map 场景)
headMap(toKey) 默认「不包含 toKey」,tailMap(fromKey) 默认「包含 fromKey」
这个不对称设计容易记混。根源在 SortedMap 接口规范:headMap 定义为 “strictly less than”,tailMap 是 “greater than or equal to”。Java 8+ 引入了带布尔参数的重载方法,才统一控制权。
-
treeMap.headMap("Banana")→ 键 "Banana" 的所有项(不含"Banana") -
treeMap.tailMap("Banana")→ 键 ≥"Banana"的所有项(含"Banana") - 要包含
"Banana"在 head 视图?用headMap("Banana", true) - 要排除
"Banana"在 tail 视图?用tailMap("Banana", false)
subMap、headMap、tailMap 都可能抛出 IllegalArgumentException
只要传入的键违反当前 TreeMap 的排序约束,比如 subMap("Z", "A") 或 headMap(null),就会立刻失败。这不是运行时异常,而是 unchecked,编译器不提醒,上线后才暴露。
- 键为
null:如果TreeMap没配Comparator且 key 类型没实现Comparable,任何范围操作都炸 - 区间非法:fromKey > toKey(自然顺序下),或 comparator 自定义逻辑中比较结果不一致
- 安全做法:调用前先用
treeMap.containsKey(key)或treeMap.floorKey(key) != null做存在性探查
性能敏感场景下,别在循环里反复调用 headMap/tailMap
每次调用都新建一个 AscendingSubMap 匿名内部类实例,虽不拷贝节点,但构造对象+维护视图边界仍有开销。高频调用(如分页查询、滑动窗口)建议缓存或改用 ceilingEntry/floorEntry 等单点导航方法。
- 错误示范:
for (int i = 0; i - 更优解:先
TreeMap一次遍历收集所需范围,或用subMap(from, true, to, false)明确边界再迭代 - 极端情况:若只需「最大/最小 N 条」,直接用
treeMap.descendingMap().values().stream().limit(N).collect(...)更直观
真正要注意的不是语法怎么写,而是你拿到的那个 headMap 或 tailMap 对象,它背后没有数据副本,只有指针和边界检查——一不留神,改的就不是“子集”,而是整个有序世界本身。










