Java:TreeSet的使用
目录
一、概念
二、常用操作
2.1 初始化排序规则
2.2 CRUD
2.3 遍历
2.4 自定义对象排序
2.5 实际应用示例:学生成绩
三、与HashSet对比
一、概念
TreeSet 实现了 Set 接口和 NavigableSet 接口(继承了 SortedSet
接口),基于 TreeMap 实现。TreeSet 中的元素是有序的,且不允许重复。
-
Set:首先,它是一个
Set
,这意味着它不允许重复元素。如果你尝试添加一个已经存在的元素,add()
方法会返回false
。 -
SortedSet:其次,它是一个
SortedSet
,这意味着它会自动对其中所有元素进行排序。 -
NavigableSet:它进一步提供了丰富的导航方法,例如查找“小于等于”、“大于等于”某个元素的最近元素等。
核心特点:
-
有序性:元素按照自然顺序或指定的比较器进行排序
-
唯一性:不允许重复元素
-
基于红黑树:底层使用 TreeMap 实现,保证了高效的查找、插入和删除操作。红黑树是自平衡二叉查找树。
-
二叉查找树 (BST):在 BST 中,每个节点都大于其左子树中的任意节点,且小于其右子树中的任意节点。这使得查找、插入和删除操作的平均时间复杂度为 O(log n)。
-
自平衡:普通的 BST 在插入有序数据时会退化成链表,操作复杂度变为 O(n)。红黑树通过一套复杂的旋转和变色规则来确保树始终保持大致平衡,从而保证了最坏情况下,查找、插入、删除的时间复杂度也是 O(log n)。
-
-
非线程安全:需要外部同步才能在多线程环境中使用
注意事项:
-
排序一致性:确保你的
Comparable
或Comparator
实现与equals()
逻辑一致,这是正确性的基础。 -
null
值:尽量避免向TreeSet
中添加null
,除非你的比较器能明确处理它。
二、常用操作
2.1 初始化排序规则
// 使用自然排序(元素必须实现 Comparable 接口)
TreeSet<String> treeSet = new TreeSet<>();
treeSet.add("Orange");
treeSet.add("Apple");
treeSet.add("Banana");
// 输出:[Apple, Banana, Orange]
System.out.println(treeSet);// 使用自定义比较器
TreeSet<String> customTreeSet = new TreeSet<>((a, b) -> b.compareTo(a)); // 逆序
customTreeSet.add("Orange");
customTreeSet.add("Apple");
customTreeSet.add("Banana");
// 输出:[Orange, Banana, Apple]
System.out.println(customTreeSet);// 举例:按字符串长度排序
TreeSet<String> lengthSet = new TreeSet<>(new Comparator<String>() {@Overridepublic int compare(String s1, String s2) {int lenDiff = s1.length() - s2.length();// 如果长度相同,再用自然排序区分,否则会被视为相同元素而无法添加return (lenDiff != 0) ? lenDiff : s1.compareTo(s2);}
});
2.2 CRUD
// 添加元素
TreeSet<Integer> numbers = new TreeSet<>();
numbers.add(5);
numbers.add(2);
numbers.add(8);
numbers.add(1);
// 输出:[1, 2, 5, 8]
System.out.println(numbers);// 删除元素
numbers.remove(2); // 删除指定元素
numbers.pollFirst(); // 删除并返回第一个元素
numbers.pollLast(); // 删除并返回最后一个元素// 查询操作
TreeSet<Integer> set = new TreeSet<>();
for (int i = 1; i <= 10; i++) {set.add(i);
}
// 基本查询
System.out.println("First: " + set.first()); // 1
System.out.println("Last: " + set.last()); // 10
// 范围查询
System.out.println("HeadSet (<5): " + set.headSet(5)); // [1, 2, 3, 4]
System.out.println("TailSet (≥5): " + set.tailSet(5)); // [5, 6, 7, 8, 9, 10]
System.out.println("SubSet (3-7): " + set.subSet(3, 7)); // [3, 4, 5, 6]
// 邻近元素查询
System.out.println("Lower(5): " + set.lower(5)); // 4
System.out.println("Floor(5): " + set.floor(5)); // 5
System.out.println("Higher(5): " + set.higher(5)); // 6
System.out.println("Ceiling(5): " + set.ceiling(5)); // 5
方法 | 描述 |
---|---|
E first() | 返回当前集合中的第一个(最低)元素。 |
E last() | 返回当前集合中的最后一个(最高)元素。 |
E lower(E e) | 返回严格小于给定元素的最大元素,没有则返回 null 。 |
E floor(E e) | 返回小于等于给定元素的最大元素,没有则返回 null 。 |
E higher(E e) | 返回严格大于给定元素的最小元素,没有则返回 null 。 |
E ceiling(E e) | 返回大于等于给定元素的最小元素,没有则返回 null 。 |
E pollFirst() | 移除并返回第一个元素,如果集合为空则返回 null 。 |
E pollLast() | 移除并返回最后一个元素,如果集合为空则返回 null 。 |
NavigableSet<E> descendingSet() | 返回一个此集合的逆序视图。 |
Iterator<E> descendingIterator() | 返回一个逆序迭代器。 |
NavigableSet<E> headSet(E toElement, boolean inclusive) | 返回此集合中小于(或等于) toElement 的部分视图。 |
NavigableSet<E> tailSet(E fromElement, boolean inclusive) | 返回此集合中大于(或等于) fromElement 的部分视图。 |
NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) | 返回此集合中从 fromElement 到 toElement 的范围视图。 |
2.3 遍历
// 使用迭代器
Iterator<Integer> iterator = set.iterator();
while (iterator.hasNext()) {System.out.println(iterator.next());
}// 使用增强for循环
for (Integer num : set) {System.out.println(num);
}// 使用降序迭代器
Iterator<Integer> descIterator = set.descendingIterator();
while (descIterator.hasNext()) {System.out.println(descIterator.next());
}
2.4 自定义对象排序
方法一:实现 Comparable 接口
要让自定义对象能够在 TreeSet 中正确排序,需要实现 Comparable 接口或提供 Comparator。
class Person implements Comparable<Person> {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}@Overridepublic int compareTo(Person other) {return Integer.compare(this.age, other.age);}@Overridepublic String toString() {return name + "(" + age + ")";}
}// 使用
TreeSet<Person> people = new TreeSet<>();
people.add(new Person("Alice", 25));
people.add(new Person("Bob", 30));
people.add(new Person("Charlie", 20));// 输出:[Charlie(20), Alice(25), Bob(30)]
System.out.println(people);
方法二:使用 Comparator
TreeSet<Person> peopleByName = new TreeSet<>(Comparator.comparing(Person::getName)
);
peopleByName.add(new Person("Alice", 25));
peopleByName.add(new Person("Bob", 30));
peopleByName.add(new Person("Charlie", 20));// 输出:[Alice, Bob, Charlie]
2.5 实际应用示例:学生成绩
public class TreeSetExample {public static void main(String[] args) {// 创建一个存储学生成绩的TreeSetTreeSet<Student> students = new TreeSet<>(Comparator.comparing(Student::getScore).reversed().thenComparing(Student::getName));students.add(new Student("Alice", 85));students.add(new Student("Bob", 92));students.add(new Student("Charlie", 85));students.add(new Student("David", 78));// 输出前三名System.out.println("Top 3 students:");Iterator<Student> it = students.iterator();for (int i = 0; i < 3 && it.hasNext(); i++) {System.out.println((i + 1) + ". " + it.next());}// 查找成绩在80分以上的学生System.out.println("\nStudents with score ≥ 80:");Student dummy = new Student("", 80);for (Student s : students.tailSet(dummy)) {System.out.println(s);}}
}class Student {private String name;private int score;public Student(String name, int score) {this.name = name;this.score = score;}public String getName() { return name; }public int getScore() { return score; }@Overridepublic String toString() {return name + ": " + score;}
}
三、与HashSet对比
特性 | TreeSet | HashSet |
---|---|---|
底层数据结构 | 红黑树 | 哈希表 |
元素顺序 | 有序(按比较规则排序) | 无序(不保证迭代顺序) |
add , remove , contains 性能 | O(log n) | O(1)(平均情况) |
允许 null | 不允许(取决于Comparator) | 允许一个 null 元素 |
比较方式 | 使用 compareTo() 或 Comparator | 使用 hashCode() 和 equals() |
接口实现 | NavigableSet , SortedSet | Set |