您的位置:首页 > 其它

自定义类的对象作为TreeSet元素的两种方法排序浅谈

2015-03-25 20:59 519 查看
我们知道TreeSet是Set接口下的一个实现类,TreeSet中的元素的排列顺序是和添加的顺序是没有关系的,并且它里面元素也是不能重复的

但是,TreeSet集合有它独有的特点,比如:

import java.util.*;
public class Test{
public static void main(String[] args){
TreeSet ts = new TreeSet();
ts.add("x");
ts.add("a");
ts.add("c");
ts.add("j");
System.out.println(ts);
}
}


看输出结果:



集合里元素的排序,虽然和你添加的顺序无关,但它会根据元素的unicode自然排序

但是,当你给集合中添加不同类型的数据时:

import java.util.*;
public class Test{
public static void main(String[] args){
TreeSet ts = new TreeSet();
ts.add("x");
ts.add("a");
ts.add("c");
ts.add(1);
System.out.println(ts);
}
}


出现的结果是:



就会报ClassCastException 类型转换异常

String 不能转成Integer类型,要排序,类型必须一致

说了这么多,TreeSet的排序好像就这么多了??

当然不是了,再往下看:

如果现在给集合里添加自定义类的对象怎么办?它又是怎么排序的呢?

import java.util.*;

class Person{
private String name ;
private int age;
public Person(String name,int age){
this.name = name;
this.age = age;
}
public String toString(){
return "Person[name="+name+",age="+age+"]";
}
}
public class Test{
public static void main(String[] args){
TreeSet ts = new TreeSet();
ts.add(new Person("ab",20));
ts.add(new Person("ac",10));
ts.add(new Person("aa",20));
ts.add(new Person("ad",50));
System.out.println(ts);
}
}


再看运行结果:



可以看出,给出的异常信息是:Person类不能比较,为什么呢?

它给的异常信息指示是应该去实现Comparable接口,一般系统内置的类都已经实现了Comparable接口,像上面的String类,Integer类

不卖关子了,说一下,自定义类的对象作为TreeSet元素的两种方法排序

第一种:让自定义的类实现Comparable接口,并实现里面的compareTo()抽象方法,此时自定义的类的对象就有了可比性

第二种:当自定义的类没有实现Comparable接口时,这时自定义的类的对象也就不具备可比性了,这时需要让集合自身具备可比性,在集合初始化时,可以指明集合的比较方式,即在new TreeSet()时,在里面写一个匿名内部类,该匿名内部类实现Comparator接口,再重写它的compare()方法,具体看代码

先看第一种:

看下面的代码,排序规则是自定义的,先按照年龄排序,在年龄相同的情况下,再按名字排序

import java.util.*;
class Person implements Comparable{
private String name ;
private int age;
public Person(String name,int age){
this.name = name;
this.age = age;
}
public String toString(){
return "Person[name="+name+",age="+age+"]";
}
// compareTo()方法是一个回调方法!
// 返回值有3种情况:
// 1. 返回大于0的数字,表示传入的参数比当前对象大
// 2. 返回小于0的数字,表示传入的参数比当前对象小
// 3. 返回0,表示当前对象等于传入的参数
public int compareTo(Object obj){
if(!(obj instanceof Person)){
//这里先判断Obj是不是Person的实例,如果不是直接抛异常
//不用再进行下面的比较
throw new ClassCastException("这不是人");
}
Person p = (Person)obj;
int n = this.age - p.age;
//先按年龄比,年龄相同时,再按名字比
if(n == 0){
return this.name.compareTo(p.name);
}
return n;
}
}
public class Test{
public static void main(String[] args){
Set set = new TreeSet();
set.add(new Person("ab",20));
set.add(new Person("ac",10));
set.add(new Person("aa",20));
set.add(new Person("ad",50));
System.out.println(set);
}
}


看输出结果:



看中间两项,当年龄相同,都是20,此时,两者再按照名字排序

第二种:

此时,不实现Comparable接口,在new TreeSet()时,在里面写一个匿名内部类,该匿名内部类实现Comparator接口,再重写它的compare()方法

import java.util.*;
class Person{
private String name ;
private int age;
public Person(String name,int age){
this.name = name;
this.age = age;
}
public String toString(){
return "Person[name="+name+",age="+age+"]";
}
public int getAge(){
return age;
}
public String getName(){
return name;
}
}
public class Test{
public static void main(String[] args){
Set set = new TreeSet(new Comparator(){
//这里的compare()传两个参数
//第一种方法里的compareTo()传一个参数
public int compare(Object obj1,Object obj2){
if(!((obj1 instanceof Person)&&(obj1 instanceof Person))){
throw new ClassCastException("这不是人");
}
Person p1 = (Person)obj1;
Person p2 = (Person)obj2;
System.out.println(p1 + "vs" + p2);
return p1.getAge()-p2.getAge();
}
});
set.add(new Person("ab",20));
set.add(new Person("ac",10));
set.add(new Person("aa",20));
System.out.println(set);
}
}


看输出结果:



最后一行是最终结果,前面三行是比较过程

这里只按照年龄排序,没有再按名字排序,所以最后输出的set只有两项,第三项和第一项重复,直接被过滤掉

注意一点,当两种方法都存在时,到底以哪种方法为主呢?

答案是,以第二种方法,以比较器comparator()为主

我们可以验证一下:看下面,把两个方法都放进去

import java.util.*;
class Person implements Comparable{
//这里实现了Comparable接口
private String name ;
private int age;
public Person(String name,int age){
this.name = name;
this.age = age;
}
public String toString(){
return "Person[name="+name+",age="+age+"]";
}
public int getAge(){
return age;
}
public String getName(){
return name;
}

public int compareTo(Object obj){
if(!(obj instanceof Person)){

throw new ClassCastException("这不是人");
}
Person p = (Person)obj;
int n = this.age - p.age;

if(n == 0){
return this.name.compareTo(p.name);
}
return n;
}
}
public class Test{
public static void main(String[] args){
Set set = new TreeSet(new Comparator(){
//这里也实现了Comparator接口
//那么以哪种为主呢?
//看最下面的结果
public int compare(Object obj1,Object obj2){
if(!((obj1 instanceof Person)&&(obj1 instanceof Person))){
throw new ClassCastException("这不是人");
}
Person p1 = (Person)obj1;
Person p2 = (Person)obj2;
return p1.getAge()-p2.getAge();
}
});
set.add(new Person("ab",20));
set.add(new Person("ac",10));
set.add(new Person("aa",20));

System.out.println(set);
}
}


如果输出的集合中有三项,则说明以第一种为主;

如果输出的集合中有两项,则说明以第二种为主

最终结果为:



输出的只有两项,说明了:当两种排序方式都存在时,以比较器为主

其实第一种方式是有安全隐患的,后续再来完善…..
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐