TreeSet
阅读这篇文章只需5-10分钟。
TreeSet是SortedSet接口的实现类,正如SortedSet名字暗示的,TreeSet可以确保集合元素处于排序状态。与HashSet集合相比,TreeSet还提供了如下几个额外方法:
- Comparator comparator();如果TreeSet采用了定制排序,则该方法返回定制排序所使用Comparator;如果TreeSet采用了自然排序,则返回null;
- Object first();返回集合中的第一个元素;
- Object last();返回集合中的最后一个元素;
- Object lower(Object o);返回集合中位于指定元素之前的元素(即小于指定元素的最大元素,参考元素不需要是TreeSet集合里的元素);
- Object higher(Object o);返回集合中位于指定元素之后的元素(即大于指定元素的最小元素,参考元素不需要是TreeSet集合里的元素);
- SortedSet subSet(Object fromElement,Object toElement);返回此Set的子集合,返回从fromElement,Object (包含到)toElement(不包含);
- SortedSet headSet(Object toElement);返回此Set的子集,由小于toElement的元素组成;
- SortedSet tailSet(Object fromElement);返回此Set的子集,由大于或等于fromElement的元素组成;
示例:
package com.collection;
import java.util.TreeSet;
public class TreeSetTest {
public static void main(String[] args) {
TreeSet set = new TreeSet();
set.add(9);
set.add(-2);
set.add(3);
set.add(10);
System.out.println(set);//[-2, 5, 9, 10]
//输出集合中第一个元素
System.out.println("set.first() = "+set.first());//set.first() = -2
//输出集合中最后一个元素
System.out.println("set.last() = "+set.last());//set.last() = 10
//返回小于4的子集,不包含4
System.out.println("set.headSet() = "+set.headSet(4));//set.headSet() = [-2, 3]
//返回大于5的子集,包含5
System.out.println("set.tailSet() = "+set.tailSet(5));//set.tailSet() = [9, 10]
//返回大于等于-3,小于4的子集
System.out.println("set.subSet() = "+set.subSet(-3, 4));//set.subSet() = [-2, 3]
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
与HashSet集合采用通过hash算法来决定元素的存储位置不同,TreeSet采用红黑树的数据结构来存储集合元素。
TreeSet支持两只排序方法:自然排序与定制排序。
自然排序:
TreeSet会调用集合元素的compareTo(Object o)方法来比较元素之间的大小关系,然后将集合元素按升序排列,这种方式是自然排序。
Java提供了一个Comparable接口,该接口定义一个compareTo(Object o)方法,该方法返回一个整数值,实现该接口的类必须实现该方法,实现该接口的类的对象就可以比较大小。当一个对象调用该方法与另一个对象进行比较时,例如:obj1.compareTo(obj2),如果该方法返回0,则代表这两个对象相等;如果该方法返回一个正整数,则表明obj1大于obj2;如果该方法返回一个负整数,则表明obj1小于obj2。
特别篇:
Java的一些常用类已经实现了Comparable接口,并提供了比较大小的标准。下面是视线了Comparable接口的常用类。
1 BigDecimal、BigInterger以及所有的数值型对应的包装类:按它们对应的数值大小进行比较。
2 Character: 按字符的UNICODE值进行比较。
3 Boolean: true对应的包装示例大于false对应的包装类实例。
4 String: 按字符串中字符的UNICODE值进行比较。
5 Date、Time: 后面的时间、日期比前面的时间、日期大。
问题1:
如果试图把一个对象添加到TreeSet时,则该对象的类必须实现Comparable接口,否则程序会抛出异常java.lang.ClassCastException。
如下:
public static void main(String[] args) {
TreeSet t1= new TreeSet();
t1.add(new Err());
t1.add(new Err());
//Exception in thread "main" java.lang.ClassCastException:
}
class Err{
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
原因:
TreeSet集合中添加两个Err对象,添加第一个对象时,TreeSet里没有任何元素,所以不会出现任何问题;当添加第二个Err对象时,TreeSet就会调用该对象的compareTo(Object obj)方法与集合中的其他元素进行比较—如果其对应的类没有实现Comparable 接口,则会引发ClassCastException异常。
问题2:
在实现compareTo(Object o)方法时,都需要将被比较对象obj强制类型转换成相同类型,因为只有相同类的两个示例才会比较大小。
当试图把一个大对象添加到TreeSet集合时,TreeSet会调用该对象的compareTo(Object o)方法与集合中的其他元素进行比较—这就要求集合中的其他元素与该元素是同一类的示例,否则抛出ClassCaseException异常。
总之一句话,如果希望TreeSet能够正常运行,TreeSet只能添加同一类型对象。
错误示例:
public static void main(String[] args) {
TreeSet t2 = new TreeSet();
t2.add("hello");
t2.add(1);
//java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
TreeSet判断标准:
TreeSet判断两个对象是否相等的唯一标准是:两个对象通过compareTo(Object o)方法比较是否返回0 —如果通过compareTo(Object o)方法比较返回0,TreeSet则会认为它们相等:否则就认为它们不相等。
示例:
我们定义一个类z ,实现compareTo(Object o )方法,重写equals()方法,TreeSet判断两个对象的标准上面已经讲了,它会已两个对象的compareTo(Object o )比较的返回值来判断,为0时,代表两个对象相等;为1时,代表obg1大于obj2对像,否则返回-1。
class z implements Comparable{
int size;
public z( int size){
this.size = size;
}
public int compareTo(Object o) {
//永远不相等
return 1;
}
public boolean equals(Object obj) {
return true;
}
}
public static void main(String[] args) {
TreeSet t3 = new TreeSet();
z z1 = new z(6);
t3.add(z1);
t3.add(z1);
//TreeSet添加了两个z对象到集合中,并且添加成功了
System.out.println(t3);
//输出的结果显示,这两个对象实际是相等的,之所以能添加成功的原因是compareTo 返回了1,这就表示按当前的示例
无论添加对象z对象到TreeSet集合中,都会成功,因为它们始终不相等(也就是compareTo返回值 为 0)
//[com.collection.z@15db9742, com.collection.z@15db9742]
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
正确示例:
判断两个对象的是否相等的参照标准,下面我们已Size为例;
class z implements Comparable{
int size;//判断标准
public z( int size){
this.size = size;
}
public int compareTo(Object o) {
z z1 = (z)o;//类型转换
return this.size > z1.size ? 1 :this.size < z1.size ? -1 : 0;
}
public boolean equals(Object obj) {
if(this == obj)
{
return true;
}
if(obj != null && obj.getClass() == z.class)
{
z z1 = (z)obj) ;//类型转换
return z1.size == this.size;
}
return false;
}
}
public static void main(String[] args) {
TreeSet t5 = new TreeSet();
t5.add(new R1(6));
t5.add(new R1(6));
t5.add(new R1(-6));
t5.add(new R1(1));
System.out.println(t5);
//对重复的元素6进行了对比,输出结果显示只有,6、-6、1
//[com.collection.R1@6d06d69c, com.collection.R1@7852e922, com.collection.R1@4e25154f]
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
注意:
与HashSet类似的是,如果TreeSet中包含了可变对象,当可变对象的示例变量被修改时,TreeSet在处理这些对象时将非常复杂,而且容易出错。为了让程序更加健壮,推荐不要修改放入HashSet和TreeSet集合中元素的关键实例变量。
定制排序
TessSet 的自然排序是根据集合元素的大小,TreeSet将它们以升序排列。如果需要实现定制排序,例如以降序排列,则可通过Comparator接口的帮助。该接口里包含一个int compate(T o1,T o2)方法,该方法用于比较o1和o2的大小:如果该方法返回正整数,则表明o1大于o2;如果该方法返回0,则表明o1等于o2;如果该方法返回负整数,则表明o1小于o2。
示例:
class R2 implements Comparable{
int size;//判断标准
public R2( int size){
this.size = size;
}
public int compareTo(Object o) {
R2 r = (R2)o;//类型转换
return this.size > r.size ? -1 :this.size < r.size ? 1 : 0;
}
public boolean equals(Object obj) {
if(this == obj){
return true;
}
if(obj != null && obj.getClass() == z.class){
R2 r = (R2)obj;//类型转换
return r.size == this.size;
}
return false;
}
}
public static void main(String[] args) {
TreeSet t6 = new TreeSet();
t6.add(new R2(6));
t6.add(new R2(6));
t6.add(new R2(-6));
t6.add(new R2(1));
System.out.println(t6);
R2 r1 = (R2) t6.first();
System.out.println("r1.first() = "+r1.size);//set.first() = 6
//输出集合中最后一个元素
R2 r2 = (R2) t6.last();
System.out.println("r2.last() = "+r2.size);//set.last() = -6
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
Things won are done; joy’s soul lies in the doing.
得到即是完结,快乐的精髓在于过程。