Java TreeSet
Java中的TreeSet是Set接口的实现之一。它与其他流行的实现HashSet的不同之处在于,它与无序的HashSet不同,TreeSet以排序的顺序存储其元素。
TreeSet中的元素使用其自然顺序或者在集合创建时提供的Comparator进行排序,具体取决于使用哪个构造函数来创建TreeSet。
Java中的TreeSet实现
如果我们对Java中的HashSet内部实现有所了解,那么我们必须知道HashSet在内部使用HashMap存储其元素。 TreeSet在内部使用TreeMap的方式相同。 Java中的TreeSet是基于TreeMap的NavigableSet实现。
Java中TreeSet的功能
这篇文章中讨论的TreeSet的一些功能如下:
- TreeSet仅存储唯一的元素,就像其他Set实现一样。
- TreeSet按排序顺序存储其元素。
- 在TreeSet中不允许使用null元素。
- TreeSet不是线程安全的。
- TreeSet类的迭代器方法返回的迭代器是快速失败的。这意味着,如果在创建迭代器之后的任何时候修改了集合,则除了通过迭代器自己的remove方法之外,它会以任何方式进行修改,迭代器都会引发ConcurrentModificationException。
Java TreeSet构造函数
Java中的TreeSet类具有4个构造函数。
- TreeSet()–构造一个新的空树集,并根据其元素的自然顺序进行排序。
- TreeSet(Collection <?extends E> c)–构造一个包含指定集合中元素的新树集,并根据其元素的自然顺序对其进行排序。
- TreeSet(Comparator <?super E>比较器)–构造一个新的空树集,根据指定的比较器排序。
- TreeSet(SortedSet s)–构造一个新的树集,其中包含与指定的排序集相同的元素,并使用相同的顺序。
创建TreeSet的Java示例
本示例说明如何创建TreeSet以及如何向其中添加元素。
import java.util.Set; import java.util.TreeSet; public class TreeSetDemo { public static void main(String[] args) { // creating TreeSet Set<String> carSet = new TreeSet<String>(); carSet.add("Audi"); carSet.add("Mini Cooper"); carSet.add("Jaguar"); carSet.add("BMW"); carSet.add("Mini Cooper"); for(String car : carSet) { System.out.println("car name- "+ car); } } }
输出:
car name- Audi car name- BMW car name- Jaguar car name- Mini Cooper
如我们所见,输出元素按照字符串的自然顺序升序排序。同样,重复元素仅添加一次。
TreeSet中没有空
尽管其他Set实施(例如HashSet和LinkedHashSet)允许添加null作为元素,但Java中的TreeSet不允许null。如果尝试添加null,它将抛出NullPointerExcpetion。
public class TreeSetDemo { public static void main(String[] args) { // creating TreeSet Set<String> carSet = new TreeSet<String>(); carSet.add("Audi"); carSet.add("Mini Cooper"); carSet.add("Jaguar"); carSet.add("BMW"); carSet.add(null); for(String car : carSet) { System.out.println("car name- "+ car); } } }
输出:
Exception in thread "main" java.lang.NullPointerException at java.base/java.util.TreeMap.put(TreeMap.java:561) at java.base/java.util.TreeSet.add(TreeSet.java:255) at com.theitroad.TreeSetDemo.main(TreeSetDemo.java:15)
Java TreeSet类中的方法
Java中TreeSet类的一些方法。
- ceiling(E e)–返回此集合中大于或者等于给定元素的最小元素;如果没有此类元素,则返回null。
- DescendingIterator()–以降序返回此集合中元素的迭代器。
- DescendingSet()–返回此集合中包含的元素的逆序视图。
- floor(E e)–返回此集合中小于或者等于给定元素的最大元素;如果没有此类元素,则返回null。
- 更高的(E e)–返回此集合中最小的元素,该元素严格大于给定的元素;如果没有这样的元素,则返回null。
- tailSet(E fromElement)–返回此集合中元素大于或者等于fromElement的部分的视图。
- tailSet(E fromElement,包含布尔值)–返回此集合中元素大于(或者等于,如果包含为true的话)fromElement的部分的视图。
使用比较器以不同顺序对TreeSet元素进行排序
如果我们想要自然排序以外的其他排序,则可以提供自己的比较器。
public class TreeSetDemo { public static void main(String[] args) { // creating TreeSet with Comparator Set<Integer> numberSet = new TreeSet<Integer>( (Integer num1, Integer num2)-> num2.compareTo(num1)); numberSet.add(14); numberSet.add(2); numberSet.add(67); numberSet.add(32); numberSet.add(9); for(Integer num : numberSet) { System.out.println("number- "+ num); } } }
输出:
number- 67 number- 32 number- 14 number- 9 number- 2
在这里,使用TreeSet的构造函数,该构造函数将Comparator作为参数,并将Comparator实现为Lambda表达式以反转排序顺序。
TreeSet不是线程安全的
Java中的TreeSet由于未同步,因此不是线程安全的。如果多个线程同时访问TreeSet,并且至少有一个线程在结构上修改了Set,则必须在外部对其进行同步。我们可以使用Collections.synchronizedSortedSet()方法包装TreeSet。
SortedSet s = Collections.synchronizedSortedSet(new TreeSet());
TreeSet的性能
由于TreeSet是基于树的实现,因此为基本操作(添加,删除和包含)提供了保证的log(n)时间成本。但这比其他实现HashSet和LinkedHashSet慢,因为增加了保持元素排序的功能。