前面两节分别介绍了Set接口的实现类HashSet和LinkedHashSet类。本节介绍TreeSet类。
TreeSet 也是一个有序的集合,TreeSet使用二叉树的原理对加入的元素按照升序或降序排序,该类对新添加的元素都会进行排序,将元素插入到二叉树指定的位置。
下面先通过一个应用实例对TreeSet的用法有个具体了解。
package treeset;
import java.util.TreeSet;
public class TreeSetTest {
public static void main(String[] args) {
TreeSet<Integer> 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]
}
}与HashSet集合采用通过hash算法来决定元素的存储位置不同,TreeSet采用二叉树的数据结构来存储集合元素。

图 14-6 TreeSetTest示例输出结果
从输出结果可以看出,TreeSet对加入元素默认按照升序进行排序。
TreeSet的排序原理
TreeSet调用元素的compareTo方法来比较元素之间的大小关系,然后将元素按升序或降序排列,因此待加入的元素必须实现compareTo方法,并通过compareTo方法比较两个元素内容的大小。
待加入的元素如何实现compareTo方法呢?Java提供了一个Comparable接口,该接口定义了一个compareTo(Object o)方法,用于比较两个元素的大小,并返回一个整数值。需要加入TreeSet集合的元素必须要实现该接口,该接口的实现代码用于比较当前元素与传入元素的大小进行比较,并返回结果。因此实现该接口类的对象就可以比较大小。当一个对象调用该方法与另一个对象进行比较时,该方法就可以返回比较结果。例如:tempObj1.compareTo(obj2),如果该方法返回0,则代表这两个对象相等;如果该方法返回1,则表明tempObj1大于obj2;如果该方法返回-1,则表明tempObj1小于obj2。
需要加入TreeSet集合的元素必须实现Comparable接口,如果试图把没有实现Comparable接口的类对象加入TreeSet时,程序就会抛出异常。
没有实现Comparable接口类的程序代码如下:
package treesetnum;
import java.util.TreeSet;
public class MainTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
TreeSet<MyNumber> set = new TreeSet();
set.add(new MyNumber(12));
set.add(new MyNumber(8));
set.add(new MyNumber(19));
}
}
// MyNumber类
package treesetnum;
public class MyNumber {
public int num;
public MyNumber(int innum)
{
num = innum;
}
public void OutNum()
{
System.out.println("序号:" + num);
}
}MyNumber为自定义的序号类,该类没有实现Comparable接口。程序在MainTest类的main方法中声明了TreeSet集合,并添加MyNumber对象。运行上述代码后,程序抛出错误,如下图所示:

图 14-7 MainTest程序输出结果
从输出结果可以看出,程序抛出“Exception in thread "main" java.lang.ClassCastException: treesetnum.MyNumber cannot be cast to java.lang.Comparable”异常,意思是MyNumber类没有实现Comparable接口,导致进行元素比较时发生错误。
在上面的例子代码中,TreeSet集合添加了三个MyNumber对象,添加第一个对象时,TreeSet里没有任何元素,所以不会出现任何问题,当添加第二个MyNumber对象时,TreeSet就会调用该对象的compareTo(Object obj)方法与集合中的其他元素进行比较,如果其对应的类没有实现Comparable 接口,则会引发ClassCastException异常。
修改上面的代码如下:
package treesetnum;
import java.util.Iterator;
import java.util.TreeSet;
public class MainTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
TreeSet<MyNumber> set = new TreeSet();
set.add(new MyNumber(12));
set.add(new MyNumber(8));
set.add(new MyNumber(19));
//输出集合元素
Iterator<MyNumber> i = set.iterator();
while(i.hasNext()) {
MyNumber temp = (MyNumber)i.next();
temp.OutNum();
}
}
}
// MyNumber类
package treesetnum;
public class MyNumber implements Comparable{
public int num;
public MyNumber(int innum)
{
num = innum;
}
public void OutNum()
{
System.out.println("序号:" + num);
}
@Override
public int compareTo(Object o) {
// TODO Auto-generated method stub
MyNumber inMyNumner = (MyNumber)o;
// 大于返回1
if( this.num > inMyNumner.getNum() )
return 1;
//小于返回-1
if( this.num < inMyNumner.getNum() )
return -1;
//相等返回0
return 0;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
}修改后的代码在MyNumber类中实现了Comparable接口,实现方法名为compareTo,该方法实现两个类属性num的大小比较。如果当前num大于传入的num,该方法返回1;如果当前num小于传入的num,该方法返回-1;若相等则返回0。
修改后的代码输出结果如下图所示:

图 14-8 修改后代码输出结果
从图中可以看出,修改后的代码可以正常地加入元素,并能对加入的元素进行正确排序。
■ 知识点拨
在实际编程中,需要注意的是放入TreeSet集合中的元素必须是可“排序”的,对加入的元素(自定义类),若要实现compareTo方法,必须实现Comparable接口。在Java SDK中,已经有一些实现了Comparable接口的类,其中包括String字符串类,还包括Float、Interger、Long、Short、Double等数字类。