JAVA集合之——TreeSet
2015-07-01 15:04
465 查看
JAVA集合之——TreeSet
一、TreeSet的排序原理
TreeSet是有序的,且不可以重复的集合,首先,进行如下测试:
对一个自定义的对像Persion进行如添加元素,打印操作,如下:
TreeSet.Persion cannot be cast to java.lang.Comparable
原来,在默认的情况下,TreeSet假定插入的元素产现了Comparable接口,关于Comparable接口,JDK文档抄录如下:
接口 Comparable<T>
类型参数:T - 可以与此对象进行比较的那些对象的类型
接口原型:
如果要插入自定义对像,就必须实现Comparable接口自定义排序规则,即实现compareTo方法,这个方法的算法如何实现,就看自已的需求了,如果按照年年龄进行排序,那么Persion类的定义如下:
实际上,在使用TreeSet存储对像时,它的add()方法是会自动的调用compareTo方法来进行对像的比较,如果相同的元素,就不存储,不相同则按照红黑树(一种自平衡的二叉树)的算法进行存储。
即,如果a和b相等,调用a.compareTo(b)一定返回0;如果排序后a位于b之前,则返回负值;如果a位于b之后,则返回正值。
对于上面的例子,如果添加一行:
set.add(new Persion("小明", 50));
经测试发现这个元素是没有被添加进去的。为什么?
上面的compareTo方法是按age进行比较的,age相同,则被认为是相同的对像,所以没有添加进去,所以对于compareTo方法的实现方式,是要根据需求来定的,经过更改,我们采用下面的方式,看起来更完善一些。
二、Comparable与Comparator的区别。
使现Comparable接口的compareTo方法有一定的局限性。对于一个给定的类,只能够实现这个接口一次。对于上面的Persion类,在一个集合中需要按age进行排序, 在另一个集中需要按name进行排序,那要怎么办? 另外,如果Persion类的实现者没有实现Comparable接口中,又该怎么办呢?
这时,Comparator就表现出了极大的灵活性了,TreeSet有一个构造方法:
TreeSet(Comparator<? super E> comparator)
--------- 构造一个新的空 TreeSet,它根据指定比较器进行排序。
如上面所示,在创建集合时,将自已实现的Comparator对像传递给TreeSet构造器,Comparator也是一个接口,它的完整描述如下:
通常,使用匿名内部类的方式将实现的Comparator对像传递给TreeSet构造器:
总结,TreeSet集合保证元素有序和唯一的原理:
1. 唯一性: 调用add()时通过比较返回是否为0来确确定元素是否相同。
2. 有序:
A: 自然排序(元素具备比较性) —— 元素所属的类实现自然排序的Comparable接口。
B: 比较器排序(集合具备比较性) —— 集合的构造方法接收一个比较器Comparator的子类对像。
关于HashSet和TreeSet的选择:
如果不需要对元素进行排序,就没必要付出使用TreeSet所带来的排序开销。
一、TreeSet的排序原理
TreeSet是有序的,且不可以重复的集合,首先,进行如下测试:
public static void main(String[] args) { //1. 创建一个存储Integer类型的TreeSet TreeSet<Integer> set = new TreeSet<Integer>(); //无序添加对像 set.add(40); set.add(10); set.add(10); set.add(30); set.add(20); //打印 //从结果来看数据是有序输出的,并且重复的对像不会被添加 //TreeSet是一个Sorted Collection for(int x: set) { System.out.println(x); } //2. 创建一个存储String类型的TreeSet TreeSet<String> set1 = new TreeSet<>(); //无序添加对像 set1.add("Hello"); set1.add("World"); set1.add("World"); set1.add("JAVA SE"); set1.add("JAVA EE"); //打印 //打印结果是按字符串排序的,并且得复的元素不会被添加 for(String s: set1) { System.out.println(s); } }经过测试发现,TreeSet对插入的元素进行了排序,并且不可插入重复的元素,那TreeSet是怎样做到的呢?
对一个自定义的对像Persion进行如添加元素,打印操作,如下:
public static void main(String[] args) { //创建一个TreeSet TreeSet<Persion> set = new TreeSet<>(); //添加自定义元素 set.add(new Persion("张三", 50)); set.add(new Persion("李四", 30)); set.add(new Persion("麻子", 20)); set.add(new Persion("麻子", 40)); //打印测试 for(Persion p: set) { System.out.println(p.getName() + p.getAge()); } }然而,当运行的时候,出错了,错误信息如下:
TreeSet.Persion cannot be cast to java.lang.Comparable
原来,在默认的情况下,TreeSet假定插入的元素产现了Comparable接口,关于Comparable接口,JDK文档抄录如下:
接口 Comparable<T>
类型参数:T - 可以与此对象进行比较的那些对象的类型
接口原型:
public interface Comparable<T> { int compareTo(T other); }此接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的compareTo 方法被称为它的自然比较方法。实现此接口的对象列表(和数组)可以通过 Collections.sort(和 Arrays.sort)进行自动排序。 实现此接口的对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。对于上面的Iteger类型按大小排序,对于String类,它的commpareTo方法依据字典序对字符串进行比较。这些类是JAVA标准类库,已经实现了Comparable接口。
如果要插入自定义对像,就必须实现Comparable接口自定义排序规则,即实现compareTo方法,这个方法的算法如何实现,就看自已的需求了,如果按照年年龄进行排序,那么Persion类的定义如下:
public class Persion implements Comparable<Persion> { private String name; private int age; public Persion(String name,int age) { this.name = name; this.age = age; } public String getName() { return this.name; } public int getAge() { return this.age; } //自定义类,要实现Comparable接口的compareTo方法 public int compareTo(Persion other) { return this.age - other.age; } }如上代码所示,compareTo方法的实现是依照Persion对像的年龄比较,所以重新运行主程序代码,添加元素后,打印输出是按年龄排序的。
实际上,在使用TreeSet存储对像时,它的add()方法是会自动的调用compareTo方法来进行对像的比较,如果相同的元素,就不存储,不相同则按照红黑树(一种自平衡的二叉树)的算法进行存储。
即,如果a和b相等,调用a.compareTo(b)一定返回0;如果排序后a位于b之前,则返回负值;如果a位于b之后,则返回正值。
对于上面的例子,如果添加一行:
set.add(new Persion("小明", 50));
经测试发现这个元素是没有被添加进去的。为什么?
上面的compareTo方法是按age进行比较的,age相同,则被认为是相同的对像,所以没有添加进去,所以对于compareTo方法的实现方式,是要根据需求来定的,经过更改,我们采用下面的方式,看起来更完善一些。
public int compareTo(Persion other) { //首先按age排序 int num = this.age - other.age; //如果age相同按name排序 if(num == 0) { return this.name.compareTo(other.name); } //执行到此表示age不同,返回比较结果 return num; }经测试,name和age都相同的元素添加不进去,有一个不同的元素可以添加,验证了我们的想法。
二、Comparable与Comparator的区别。
使现Comparable接口的compareTo方法有一定的局限性。对于一个给定的类,只能够实现这个接口一次。对于上面的Persion类,在一个集合中需要按age进行排序, 在另一个集中需要按name进行排序,那要怎么办? 另外,如果Persion类的实现者没有实现Comparable接口中,又该怎么办呢?
这时,Comparator就表现出了极大的灵活性了,TreeSet有一个构造方法:
TreeSet(Comparator<? super E> comparator)
--------- 构造一个新的空 TreeSet,它根据指定比较器进行排序。
如上面所示,在创建集合时,将自已实现的Comparator对像传递给TreeSet构造器,Comparator也是一个接口,它的完整描述如下:
public interface Comparator<T> { int compare(T a,T b); }与compareTo方法一样,如果a位于b之前,compare方法返回负值;如果a和b相等,则返回0;否则返回正值。查JDK文档发现,该接口还有一个equals方法,但是不需要实现。
通常,使用匿名内部类的方式将实现的Comparator对像传递给TreeSet构造器:
TreeSet<Persion> set = new TreeSet<>(new Comparator<Persion>() { public int compare(Persion a, Persion b) { //首先按age排序 int num = a.getAge() - b.getAge(); //如果age相同按name排序 if(num == 0) { return a.getName().compareTo(b.getName()); } //执行到此表示age不同,返回比较结果 return num; } });这样的话,对于基本的Persion是固定的,而在不同的集合中却可以使用不同的排序规则对集合元素进行排序,灵活性大大提高。
总结,TreeSet集合保证元素有序和唯一的原理:
1. 唯一性: 调用add()时通过比较返回是否为0来确确定元素是否相同。
2. 有序:
A: 自然排序(元素具备比较性) —— 元素所属的类实现自然排序的Comparable接口。
B: 比较器排序(集合具备比较性) —— 集合的构造方法接收一个比较器Comparator的子类对像。
关于HashSet和TreeSet的选择:
如果不需要对元素进行排序,就没必要付出使用TreeSet所带来的排序开销。
相关文章推荐
- Java数组拼接字符串几个写法性能比较(转载)
- 【Spring学习笔记-MVC-17】Spring MVC之拦截器
- springMVC读书笔记--spring启动时web初始化
- java解压缩zip文件
- java 面向对象 7_1
- java基础之异常
- 使用Eclipse+NDK编译arm64-v8a CPU架构的speex.so文件
- spring data jpa
- 《Java并发编程实践》笔记6——并发性调优
- Java死锁范例以及如何分析死锁
- JAVA字符串的优化
- Java Exception
- Java基础复习(二)
- 关于java String的“==”和equals()比较、引用的小问题
- Java集合框架List,Map,Set等全面介绍
- 解决Eclipse启动闪退的最佳办法
- Java 基础入门随笔(6) JavaSE版——数组操作
- java压缩文件夹或文件
- JDK安装与环境变量配置
- unknow error:java.lang.NullPointerException