1. 引言
在 java 集合框架中,set 接口用于存储不重复的元素,而 sortedset 接口则在 set 的基础上增加了元素的排序能力。navigableset 接口继承自 sortedset,进一步扩展了导航方法,使得开发者可以更方便地获取集合中与目标元素最接近的元素,例如获取小于某个值的最大元素、大于某个值的最小元素等。
本文将深入讲解 navigableset 接口的定义、常用实现类、核心方法以及实际应用场景,帮助你在日常开发中更高效地使用这一强大的集合工具。
2. navigableset 接口概述
2.1 接口定义
navigableset 位于 java.util 包中,其接口定义如下:
public interface navigableset<e> extends sortedset<e> {
// 导航方法
e lower(e e);
e floor(e e);
e ceiling(e e);
e higher(e e);
// 获取子集合视图
navigableset<e> descendingset();
navigableset<e> subset(e fromelement, boolean frominclusive,
e toelement, boolean toinclusive);
navigableset<e> headset(e toelement, boolean inclusive);
navigableset<e> tailset(e fromelement, boolean inclusive);
// 获取并移除元素
e pollfirst();
e polllast();
// 迭代器
iterator<e> descendingiterator();
}
2.2 与 sortedset 的关系
navigableset 继承自 sortedset,因此它拥有 sortedset 的所有方法,如 first()、last()、comparator() 等。在此基础上,navigableset 增加了更灵活的导航查询和子集合视图操作。
2.3 主要实现类
navigableset 接口最常用的实现类是 treeset。treeset 基于红黑树实现,能够保证元素处于有序状态,并且插入、删除、查找操作的时间复杂度均为 o(log n)。
navigableset<integer> set = new treeset<>();
3. 核心导航方法详解
navigableset 提供了四个核心的导航查询方法,用于查找与目标元素最接近的元素。下面通过示例逐一说明。
3.1 lower(e e)
返回集合中严格小于 e 的最大元素;如果不存在,则返回 null。
navigableset<integer> set = new treeset<>(); set.addall(arrays.aslist(10, 20, 30, 40, 50)); // 小于 30 的最大元素是 20 integer result = set.lower(30); system.out.println(result); // 输出:20 // 小于 10 的元素不存在,返回 null system.out.println(set.lower(10)); // 输出:null
3.2 floor(e e)
返回集合中小于等于 e 的最大元素;如果不存在,则返回 null。
// 小于等于 30 的最大元素是 30 本身 integer result = set.floor(30); system.out.println(result); // 输出:30 // 小于等于 25 的最大元素是 20 system.out.println(set.floor(25)); // 输出:20
3.3 ceiling(e e)
返回集合中大于等于 e 的最小元素;如果不存在,则返回 null。
// 大于等于 30 的最小元素是 30 本身 integer result = set.ceiling(30); system.out.println(result); // 输出:30 // 大于等于 35 的最小元素是 40 system.out.println(set.ceiling(35)); // 输出:40
3.4 higher(e e)
返回集合中严格大于 e 的最小元素;如果不存在,则返回 null。
// 大于 30 的最小元素是 40 integer result = set.higher(30); system.out.println(result); // 输出:40 // 大于 50 的元素不存在,返回 null system.out.println(set.higher(50)); // 输出:null
3.5 方法对比小结
为了更直观地理解这四个方法的区别,可以参考下表:
| 方法 | 条件 | 示例(集合含 10, 20, 30, 40, 50) | 查询 30 的结果 |
|---|---|---|---|
lower(e) | 严格小于 e 的最大元素 | lower(30) | 20 |
floor(e) | 小于等于 e 的最大元素 | floor(30) | 30 |
ceiling(e) | 大于等于 e 的最小元素 | ceiling(30) | 30 |
higher(e) | 严格大于 e 的最小元素 | higher(30) | 40 |
4. 子集合视图操作
navigableset 提供了三个获取子集合视图的方法,并且允许通过布尔参数控制边界是否包含。
4.1 subset
返回从 fromelement 到 toelement 之间的子集合视图,两个边界是否包含由布尔参数决定。
navigableset<integer> set = new treeset<>(); set.addall(arrays.aslist(10, 20, 30, 40, 50)); // 包含 20,不包含 40 navigableset<integer> sub = set.subset(20, true, 40, false); system.out.println(sub); // 输出:[20, 30]
4.2 headset
返回小于(或小于等于)toelement 的子集合视图。
// 小于 40 的元素(不包含 40) navigableset<integer> head = set.headset(40, false); system.out.println(head); // 输出:[10, 20, 30] // 小于等于 40 的元素(包含 40) navigableset<integer> headinclusive = set.headset(40, true); system.out.println(headinclusive); // 输出:[10, 20, 30, 40]
4.3 tailset
返回大于(或大于等于)fromelement 的子集合视图。
// 大于等于 30 的元素(包含 30) navigableset<integer> tail = set.tailset(30, true); system.out.println(tail); // 输出:[30, 40, 50] // 大于 30 的元素(不包含 30) navigableset<integer> tailexclusive = set.tailset(30, false); system.out.println(tailexclusive); // 输出:[40, 50]
4.4 视图与原始集合的关系
需要注意的是,这些子集合视图是原集合的动态视图,对视图的修改会直接反映到原集合中,反之亦然。
navigableset<integer> set = new treeset<>(); set.addall(arrays.aslist(10, 20, 30, 40, 50)); navigableset<integer> head = set.headset(40, false); head.add(15); // 向视图添加元素 system.out.println(set); // 输出:[10, 15, 20, 30, 40, 50]
5. 获取与移除元素
navigableset 提供了 pollfirst() 和 polllast() 方法,用于获取并移除集合中的最小和最大元素。
navigableset<integer> set = new treeset<>(); set.addall(arrays.aslist(10, 20, 30, 40, 50)); // 获取并移除最小元素 integer first = set.pollfirst(); system.out.println(first); // 输出:10 system.out.println(set); // 输出:[20, 30, 40, 50] // 获取并移除最大元素 integer last = set.polllast(); system.out.println(last); // 输出:50 system.out.println(set); // 输出:[20, 30, 40]
当集合为空时,pollfirst() 和 polllast() 返回 null,不会抛出异常。
6. 反向视图与迭代
6.1 descendingset()
返回该集合的逆序视图,可以用于反向遍历或查询。
navigableset<integer> set = new treeset<>(); set.addall(arrays.aslist(10, 20, 30, 40, 50)); navigableset<integer> desc = set.descendingset(); system.out.println(desc); // 输出:[50, 40, 30, 20, 10]
6.2 descendingiterator()
返回一个逆序迭代器,用于从大到小遍历集合。
navigableset<integer> set = new treeset<>();
set.addall(arrays.aslist(10, 20, 30, 40, 50));
iterator<integer> it = set.descendingiterator();
while (it.hasnext()) {
system.out.print(it.next() + " ");
}
// 输出:50 40 30 20 10
7. 实际应用场景
7.1 实现最近邻查找
navigableset 非常适合实现「查找与目标值最接近的元素」这类需求。例如,在一个已排序的分数集合中,查找某个分数附近的学生成绩:
navigableset<integer> scores = new treeset<>();
scores.addall(arrays.aslist(58, 62, 75, 88, 93, 100));
int target = 80;
// 小于等于 80 的最高分
integer lowerorequal = scores.floor(target);
// 大于等于 80 的最低分
integer higherorequal = scores.ceiling(target);
system.out.println("目标分数:" + target);
system.out.println("不高于目标的最大分数:" + lowerorequal);
system.out.println("不低于目标的最小分数:" + higherorequal);
7.2 实现区间查询
结合 subset 方法,可以方便地实现区间查询。例如,查询成绩在 60 到 90 之间的所有学生:
navigableset<integer> scores = new treeset<>();
scores.addall(arrays.aslist(58, 62, 75, 88, 93, 100));
// 查询 [60, 90] 区间内的分数
navigableset<integer> range = scores.subset(60, true, 90, true);
system.out.println("60 到 90 之间的分数:" + range); // 输出:[62, 75, 88]
7.3 实现任务调度
navigableset 也可以用于简单的任务调度场景,例如按照优先级依次取出待处理的任务:
class task implements comparable<task> {
private int priority;
private string name;
public task(int priority, string name) {
this.priority = priority;
this.name = name;
}
@override
public int compareto(task other) {
return integer.compare(this.priority, other.priority);
}
@override
public string tostring() {
return "task{" + "priority=" + priority + ", name='" + name + "'}";
}
}
navigableset<task> taskqueue = new treeset<>();
taskqueue.add(new task(3, "写代码"));
taskqueue.add(new task(1, "修复 bug"));
taskqueue.add(new task(2, "代码评审"));
// 依次取出优先级最高的任务
while (!taskqueue.isempty()) {
task task = taskqueue.pollfirst();
system.out.println("正在处理:" + task);
}
8. 使用注意事项
8.1 元素必须可比较
navigableset 要求元素要么实现了 comparable 接口,要么在创建集合时传入 comparator 比较器。否则在添加元素时会抛出 classcastexception。
// 方式一:元素实现 comparable navigableset<integer> set1 = new treeset<>(); // 方式二:传入 comparator navigableset<string> set2 = new treeset<>(comparator.reverseorder());
8.2 不允许 null 元素
treeset 不允许添加 null 元素,因为无法对 null 进行排序比较。如果尝试添加 null,会抛出 nullpointerexception。
8.3 非线程安全
treeset 不是线程安全的。如果在多线程环境下使用,需要通过 collections.synchronizedsortedset 进行包装,或者使用并发集合类。
sortedset<integer> syncset = collections.synchronizedsortedset(new treeset<>());
8.4 视图修改的边界限制
通过 subset、headset、tailset 获取的视图,在添加元素时不能超出视图的边界范围,否则会抛出 illegalargumentexception。
navigableset<integer> set = new treeset<>(); set.addall(arrays.aslist(10, 20, 30, 40, 50)); navigableset<integer> head = set.headset(40, false); // head.add(45); // 抛出 illegalargumentexception,因为 45 超出视图边界
9. 总结
navigableset 接口在 sortedset 的基础上提供了丰富的导航方法,使得开发者可以轻松实现最近邻查找、区间查询、双向遍历等操作。其核心实现类 treeset 基于红黑树,保证了良好的性能表现。
在实际开发中,当你需要维护一个有序且不重复的元素集合,并且需要频繁进行范围查询或获取最接近元素的操作时,navigableset 是一个非常合适的选择。掌握它的核心方法,能够帮助你写出更简洁、高效的代码。
以上就是java navigableset接口从入门到实战详解的详细内容,更多关于java navigableset接口详解的资料请关注代码网其它相关文章!
发表评论