Java TreeSet

时间:2020-01-09 10:35:02  来源:igfitidea点击:

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慢,因为增加了保持元素排序的功能。