SortedSet
是一个Set
,它按元素的自然顺序或在SortedSet
创建时提供的Comparator
的升序维护其元素。除了正常的Set
操作外,SortedSet
接口还提供以下操作:
Range view
——允许对排序集进行任意范围的操作Endpoints
——返回排序集中的第一个或最后一个元素Comparator access
——返回用于对集合进行排序的Comparator
(如果有)
SortedSet
接口的代码如下。
public interface SortedSet<E> extends Set<E> {
// Range-view
SortedSet<E> subSet(E fromElement, E toElement);
SortedSet<E> headSet(E toElement);
SortedSet<E> tailSet(E fromElement);
// Endpoints
E first();
E last();
// Comparator access
Comparator<? super E> comparator();
}
Set 操作
SortedSet
从Set
继承的操作在排序集和普通集上的行为相同,但有两个例外:
iterator
操作返回的Iterator
按顺序遍历排序后的集合。toArray
返回的数组按顺序包含排序后的集合的元素。
尽管接口不能保证,但Java平台的SortedSet
实现的toString
方法按顺序返回一个字符串,其中包含排序后的集合的所有元素。
标准构造
按照惯例,所有泛型的Collection
实现都提供一个采用Collection
的标准转换构造器。SortedSet
实现也不例外。在TreeSet
中,此构造器创建一个实例,该实例根据其元素的自然顺序对其进行排序。这可能是一个错误。最好动态检查以查看指定的集合是否为SortedSet
实例,如果是,则根据相同的标准(比较器或自然排序)对新的TreeSet
进行排序。因为TreeSet
采取了它所采用的方法,所以它还提供了一个构造器,该构造器采用SortedSet
并返回一个新的TreeSet
,其中包含根据相同条件排序的相同元素。请注意,确定调用这两个构造器中的哪一个(以及是否保留排序标准)的是参数的编译时类型,而不是其运行时类型。
按照约定,SortedSet
实现还提供一个构造方法,该构造方法接受Comparator
并返回根据指定Comparator
排序的空集。如果将null
传递给此构造器,它将返回一个集合,该集合根据其自然顺序对元素进行排序。
范围视图操作
range-view
操作在某种程度上类似于List
接口提供的操作,但是有很大的不同。即使直接修改了支持排序的集合,排序的集合的范围视图仍然有效。这是可行的,因为排序集的范围视图的端点是元素空间中的绝对点,而不是后备集合中的特定元素(如列表)。排序后的集合的范围视图实际上只是该集合的任何部分位于元素空间的指定部分的窗口。对范围视图的更改将写回到后备排序集,反之亦然。因此,与列表上的range-view
不同,可以长时间在已排序的集合上使用range-view
。
排序集提供三种range-view
操作。 第一个subSet
具有两个端点,例如subList
。终结点不是对象,而是索引,它是对象,并且必须使用Set
的Comparator
或其元素的自然顺序与排序后的Set
中的元素相当,无论Set
用来对其自身进行排序。像subList
一样,范围是半开,包括其低端点,但不包括高端点。
因此,以下代码行告诉您在名为dictionary
的字符串的SortedSet
中包含“ doorbell
”和“ pickle
”之间的单词(包括“ doorbell
”,但不包括“pickle
”):
int count = dictionary.subSet("doorbell", "pickle").size();
以类似的方式,下行删除所有以字母f
开头的元素。
dictionary.subSet("f", "g").clear();
可以使用类似的技巧来打印表格,以告诉您多少个单词以每个字母开头。
for (char ch = 'a'; ch <= 'z'; ) {
String from = String.valueOf(ch++);
String to = String.valueOf(ch);
System.out.println(from + ": " + dictionary.subSet(from, to).size());
}
假设您要查看一个包含两个端点的封闭时间间隔,而不是一个开放时间间隔。如果元素类型允许计算元素空间中给定值的后继对象,则只需将subSet
从lowEndpoint
请求为successor(highEndpoint)
。尽管并不完全清楚,但按字符串自然顺序排列的字符串s
的后继者是s + "\0"
——即,s
后面附加了null
字符。
因此,下面的一句话告诉您dictionary
中“doorbell
”和“pickle
”之间有多少个单词,包括doorbell
和pickle
。
count = dictionary.subSet("doorbell", "pickle\0").size();
可以使用类似的技术来查看一个开放时间间隔,其中不包含任何端点。从lowEndpoint
到highEndpoint
的打开间隔视图是从successor(lowEndpoint)
到highEndpoint
的半打开间隔。使用以下内容计算“doorbell
”和“ pickle
”之间的单词数(不包括两者)。
count = dictionary.subSet("doorbell\0", "pickle").size();
该SortedSet
接口包含另外两个range-view
操作- headSet
和和tailSet
,这两个操作都带有一个Object
参数。前者返回支持的初始部分的视图SortedSet
,直到但不包括指定的对象。后者返回支撑的最后部分的视图SortedSet
,从指定的对象开始一直到支撑的结束SortedSet
。因此,以下代码允许您将字典视为两个不相交的volumes
(a-m
和n-z
)。SortedSet
接口包含另外两个范围视图操作——headSet
和tailSet
,这两个操作都使用单个Object
参数。前者返回支持SortedSet
的初始部分的视图,直到但不包括指定的对象。后者返回支持SortedSet
的最后部分的视图,该视图从指定的对象开始,一直到支持SortedSet
的末尾。因此,以下代码使您可以将字典视为两个不相交的volumes
(a-m
和n-z
)。
SortedSet<String> volume1 = dictionary.headSet("n");
SortedSet<String> volume2 = dictionary.tailSet("n");
端点操作
SortedSet
接口包含一些操作,以返回排序集中的第一个和最后一个元素,这并不奇怪地称为first
和last
。除了它们的明显用途外,last
还允许解决SortedSet
接口中的缺陷。您想对SortedSet
进行的一件事是进入Set
的内部并向前或向后迭代。从内部前进很容易:只需获取tailSet
并对其进行迭代。不幸的是,没有简单方法可以退后。
以下习语获得元素空间中小于指定对象o
的第一个元素。
Object predecessor = ss.headSet(o).last();
这是从已排序集合内部的某个点向后退一个元素的好方法。可以重复使用它来向后迭代,但这效率很低,需要对返回的每个元素进行查找。
Comparator访问器
SortedSet
接口包含一个称为comparator
的访问器方法,该方法返回用于对集合进行排序的Comparator
;如果该集合是根据其元素的自然顺序排序的,则返回null
。提供此方法是为了使排序后的集合可以以相同的顺序复制到新的排序后的集合中。它由前面描述的SortedSet
构造函数使用。