NavigableSet 不直接操作数组,需将有序数组转为 TreeSet 等实现类才能使用其范围查询与邻近值查找功能;核心方法包括 subSet、ceiling、floor 等,均基于红黑树实现 O(log n) 时间复杂度。

Java 中的 NavigableSet 本身不直接操作“数组”,而是提供对已排序集合的高效范围查询与邻近值查找能力;要实现对有序数组的类似功能,需先将数组转为支持 NavigableSet 的实现(如 TreeSet),再利用其内置方法——核心在于理解其语义而非手动遍历。
把有序数组转成 NavigableSet 才能用上高级能力
数组是静态、无结构的,NavigableSet 是动态、带排序索引的接口。不能直接“在数组上用 NavigableSet”,但可以低成本封装:
- 若数组已排序且只读,用
Arrays.asList(arr).stream().collect(Collectors.toCollection(TreeSet::new))或直接new TreeSet(Arrays.asList(arr))(注意:基本类型数组需先转包装类) - 若追求零拷贝且数组长期不变,可写一个轻量代理类,内部用二分查找模拟
ceiling/floor行为——但这不属于NavigableSet使用范畴 - 实际项目中,更推荐初始化一次
TreeSet,后续所有查询复用它;插入/删除少、查询频繁时性能优势明显
用 subSet、headSet、tailSet 做精准范围截取
这些方法返回的是原集合的“视图”(live view),修改会影响底层,且时间复杂度接近 O(log n) 而非 O(n):
-
set.subSet(fromElement, true, toElement, false):获取 [from, to),含 from、不含 to;第二个 boolean 控制是否包含端点 -
set.headSet(toElement, true):所有 ≤ toElement 的元素;设为false则是 -
set.tailSet(fromElement, false):所有 > fromElement 的元素;适合找“第一个大于 x 的值” - 注意:所有端点必须存在于集合中,或满足自然顺序;否则抛
IllegalArgumentException(若用TreeSet且元素不可比)
用 ceiling、floor、higher、lower 实现临近值匹配
这是最常用也最易混淆的一组方法,关键区别在“等于是否算匹配”:
-
ceiling(x)→ ≥ x 的最小元素(x 存在就返回 x,不存在返回下一个更大的) -
floor(x)→ ≤ x 的最大元素(x 存在就返回 x,不存在返回上一个小的) -
higher(x)→ > x 的最小元素(x 存在也不返回 x,严格更大) -
lower(x)→ - 全部返回
null表示无匹配项(集合为空或范围外),调用前无需判空,但业务逻辑需处理 null
结合 Stream 做复合查询(如“前 3 个大于 x 的数”)
NavigableSet 返回的视图本身是 Collection,可链式调用 stream():
-
set.tailSet(x, false).stream().limit(3).collect(Collectors.toList()):高效拿到严格大于 x 的前 3 个 -
set.headSet(x, true).stream().skip(Math.max(0, size - 5)).collect(Collectors.toList()):取 ≤ x 的最后 5 个(适合分页末尾场景) - 避免先
stream().filter(...).limit(),那会全量扫描;务必用subSet/tailSet先缩小范围

















