TreeSet是依靠TreeMap来实现的。TreeSet是一个有序集合,TreeSet中的元素将按照升序排列,缺省是按照自然排序进行排列,意味着TreeSet中的元素要实现Comparable接口。或者有一个自定义的比较器。TreeSet支持两种排序方法:自然排序和定制排序,TreeSet默认采用自然排序。
Java常用类实现Comparable接口,并提供了比较大小的标准。实现Comparable接口的常用类:BigDecimal、BigIneger以及所有数值型对应包装类:按它们对应的数值的大小进行比较;
Character:按字符的UNICODE值进行比较;
Boolean:true对应的包装类实例大于false对应的包装类实例;
String:按字符串中字符的UNICODE值进行比较;
Date、Time:后面的时间、日期比前面的时间、日期大。
1、添加一个对象到TreeSet时,对象类必须实现Comparable接口,代码如下:package com.yoodb;
import java.util.TreeSet;
public class Error{
public static void main(String[] args) {
TreeSet set = new TreeSet();
set.add(new Error());
set.add(new Error());
}
}
其结果报错,错误信息如下:Exception in thread "main" java.lang.ClassCastException: com.Error cannot be cast to java.lang.Comparable
at java.util.TreeMap.compare(TreeMap.java:1188)
at java.util.TreeMap.put(TreeMap.java:531)
at java.util.TreeSet.add(TreeSet.java:255)
at com.Error.main(Error.java:9)
注意:
1)如果向TreeSet集合中添加2个Error对象,添加第一个对象时,TreeSet里没有任何元素因此没有问题,但添加第二个Error对象时TreeSet就会调用该对象的compareTo(Object obj)方法与集合中其他元素进行比较,如果对应的类没有实现Comparable接口,则会引发ClassCastException异常;从TreeSet中取出元素第一个元素时,依然会引发ClassCastException异常。
2)采用compareTo(Object obj)方法比较对象时,都需要将被比较对象obj强制类型转换成相同类型,因为只有相同类的两个实例才能比较大小。即向TreeSet中添加的应该是同一个类的对象,否则会引发ClassCastException异常。
3)当把一个对象加入TreeSet集合中时,TreeSet调用该对象的compareTo(Object obj)方法与容器中的其他对象比较大小,然后根据红黑树算法决定它的存储位置,如果两个对象通过compareTo(Object obj)比较相等,TreeSet即认为它们存储同一位置。(如果对红黑树算法不是很了解可以去网上查询一下)
4)TreeSet集合判断两个对象不相等的标准:两个对象通过equals方法比较返回false,或通过compareTo(Object obj)比较没有返回0——即使两个对象时同一个对象,TreeSet也会把它们当成两个对象进行处理,具体代码如下:package com.yoodb;
import java.util.TreeSet;
class A implements Comparable {
int age;
public A(int age) {
this.age = age;
}
public boolean equals(Object obj) {
return false;
}
public int compareTo(Object obj) {
return 1;
}
}
public class TestTreeSet {
public static void main(String[] args) {
TreeSet set = new TreeSet();
A a1 = new A(21);
set.add(a1);
System.out.println(set.add(a1));
System.out.println(set);
((A) (set.first())).age = 25;
System.out.println(((A) (set.last())).age);
}
}
结果运行如下:true
[com.A@73cbc5cb, com.A@73cbc5cb]
25
1)把同一对象添加两次,a1对象的equals方法返回false,而且compareTo(Object obj)方法总是返回1,TreeSet会认为a1对象和它自己也不相同,因此TreeSet中添加两个a1对象。而TreeSet对象保存的两个元素实际上是同一个元素。所以当修改TreeSet集合里第一个元素的age属性后,该TreeSet集合里最后一个元素的age属性也随之改变了。
2)两个对象通过equals方法比较返回true,通过compareTo(Object obj)方法比较不返回0时TreeSet将会把这两个对象保存在不同位置,从而两个对象都可以添加成功;
3)两个对象通过equals方法比较返回false,通过compareTo(Object obj)方法比较返回0两个对象通过compareTo(Object obj)方法比较相等,TreeSet将试图把它们保存在同一个位置,但实际上又不行(否则将只剩下一个对象);
2、定制排序
实现定制排序需要在创建TreeSet集合对象时提供一个Comparator对象与该TreeSet集合关联,由该Comparator对象负责集合元素的排序逻辑。具体实现代码如下:package com.yoodb;
import java.util.Comparator;
import java.util.TreeSet;
class Age {
int age;
public Age(int age) {
this.age = age;
}
public String toString() {
return "age 对象 (age:" + age + ")";
}
}
public class TestTreeSet {
public static void main(String[] args) {
TreeSet ts = new TreeSet(new Comparator() {
public int compare(Object o1, Object o2) {
Age a1 = (Age) o1;
Age m2 = (Age) o2;
if (a1.age > m2.age) {
return -1;
} else if (a1.age == m2.age) {
return 0;
} else {
return 1;
}
}
});
ts.add(new Age(21));
ts.add(new Age(25));
ts.add(new Age(-9));
System.out.println(ts);
}
}
运行程序结果如下:[M 对象 (age:25), M 对象 (age:21), M 对象 (age:-9)]
创建一个Comparator接口的匿名内部类对象,负责ts集合的排序。当把Age对象添加到集合中时,无须Age类实现Comparable接口,此时TreeSet无须通过Age对象来比较大小,而是由与TreeSet关联的Comparator对象来负责集合元素的排序。使用定制排序时,TreeSet对集合元素排序时不管集合元素本身的大小,而是由Comparator对象负责集合元素的排序规则。