navigablemap的ceilingkey()在key大于所有键时返回null,符合接口契约;treeset中higher()找严格大于、ceiling()找大于等于;submap()需显式指定端点包含性;navigableset不支持索引访问和增强for中直接remove。

为什么 NavigableMap 的 ceilingKey() 有时返回 null 而不是你预期的键
这不是 bug,是边界行为:当传入的 key 大于所有已有键时,ceilingKey() 必须返回 null(按接口契约),而不是抛异常或 wrap-around。
常见错误现象:TreeMap<string integer> map = new TreeMap(); map.put("b", 1); map.ceilingKey("c"); // 返回 null,不是 "b"</string> —— 因为 "c" > "b",且无更大键。
- 使用场景:查找“不小于某值的最小键”,常用于时间范围对齐、价格档位匹配
- 注意
floorKey()行为相反:key 小于所有键时也返回null - 若需 fallback 到最大/最小键,得手动判断
null后调用lastKey()或firstKey() - 性能影响:所有导航方法都是 O(log n),但反复判空 + fallback 会多一次树遍历
TreeSet 实现 NavigableSet 时,higher() 和 ceiling() 的关键区别在哪
higher() 找“严格大于”,ceiling() 找“大于等于”——仅此一字之差,结果可能完全不同。
示例:TreeSet<integer> set = new TreeSet(Arrays.asList(1, 3, 5)); set.higher(3); // 返回 5;set.ceiling(3); // 返回 3</integer>
立即学习“Java免费学习笔记(深入)”;
- 容易踩的坑:误用
higher()替代ceiling()导致跳过目标元素,尤其在去重逻辑或精确匹配场景中出错 - 参数差异:两者都接受同类型元素,但语义不可互换;编译器不报错,运行时逻辑偏移
- 兼容性:所有 JDK 6+ 的
TreeSet都支持,但ConcurrentSkipListSet同样实现该接口,行为一致
用 NavigableMap.subMap() 截取区间时,开闭区间怎么控制
必须显式指定是否包含端点,靠两个布尔参数:fromInclusive 和 toInclusive。漏掉一个就可能少一条或多一条数据。
示例:map.subMap(10, true, 20, false) 表示 [10, 20),即含 10、不含 20。
- 常见错误现象:日志分析中按毫秒时间戳切片,用
subMap(start, end)(旧版重载)导致end被排除,而你以为是闭区间 - Java 6 引入三参数重载
subMap(fromKey, fromInclusive, toKey, toInclusive),推荐只用这个,避免歧义 - 性能影响:返回的是原
TreeMap的视图(view),不复制数据,但每次访问仍需 log(n) 定位边界 - 注意:视图修改会影响原 map,反之亦然;若需独立副本,得手动
new TreeMap(subMap(...))
为什么 NavigableSet 没有 get(index),也不能用增强 for 遍历时安全删除
因为它是基于比较器的有序结构,不是索引驱动;增强 for 本质调用 iterator(),而迭代中直接调用 remove() 会触发 ConcurrentModificationException。
正确做法只有两种:Iterator.remove() 或 removeIf()。
- 错误写法:
for (String s : set) { if (s.startsWith("a")) set.remove(s); }→ 运行时报错 - 正确写法:
Iterator<string> it = set.iterator(); while (it.hasNext()) { if (it.next().startsWith("a")) it.remove(); }</string> - 替代方案:
set.removeIf(s -> s.startsWith("a"))(JDK 8+,内部也是用迭代器) - 复杂点在于:
NavigableSet不提供随机访问,想按排名取第 N 个元素?只能用stream().skip(N).findFirst(),O(n) 时间——别指望它像ArrayList那样快










