Comparable指定排序
再美不及姑娘你 人气:0练习:
存储学生对象并遍历,创建TreeSet集合使用无参构造方法,并按照年龄从小到大的顺序排序,若年龄相同再按照姓名的字母顺序排序
分析:
- 1.创建学生类,成员变量
name,age
;无参构造,带参构造;get\set方法; - 2.创建测试类,添加数据并进行排序;直接排序会报错
- 3.需要
Student
实现comparable
接口并重写Comparable中的compareto方法来实现按照我们给定的顺序排序
Student类代码:
public class Student{ //成员变量 private String name; private int age; //构造方法 public Student(){} public Student(String name,int age){ this.age=age; this.name=name; } //get\set方法 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
测试类:
public class StudentDemo { public static void main(String[] args) { //创建TreeSet对象 TreeSet<Student>ts=new TreeSet<Student>(); //创建学生对象 Student s=new Student("张三",18); Student s1=new Student("张四",17); Student s2=new Student("张五",19); Student s3=new Student("张六",12); //添加数据 ts.add(s); ts.add(s1); ts.add(s2); ts.add(s3); //遍历 for (Student ss:ts){ System.out.println(ss.getName()+ss.getAge()); } } }
第一次运行结果提示Student cannot be cast to java.lang.Comparable
,这个时候我们就需要在Student类实现comparable
接口重写compareto方法,并给定返回值
public class Student implements Comparable<Student>{ //成员变量 private String name; private int age; //构造方法 public Student(){} public Student(String name,int age){ this.age=age; this.name=name; } //get\set方法 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public int compareTo(Student o) { // return 0; // return 1; // return-1; } }
那么可以看到compareto方法中有三个返回值分别是0、1、-1三种情况;
- 1.return 0:返回值是0的情况下再遍历集合只会在控制台打印出第一个元素;这是因为存入第一个元素时不需要比较直接存入集合,第二个 元素再存入是就需要跟第一个元素比较,但返回值为0,就会认为第二个元素跟第一个元素是相同的、重复的,就不存储,依此类推
- 2.return 1:返回值是1的情况下再遍历集合会按照存储数据的顺序在控制台全部打印出来;同样的,第一个元素存入不比较,第二个元素与第一个元素比较,返回值为1;就会认为第二个元素比第一个元素大,排在第一个元素后面,以此类推
- 3.return -1:与renturn 1的情况相反,也就是会按照存储数据顺序的倒序方式在控制台打印出来
思考:我们需要按照年龄的大小排序,这本质上不是只要返回值是一个正数就行了嘛,那我们就可以在compareto
方法中这样写
public int compareTo(Student s) { //return 0; //return 1; int num=this.age-s.age; return num; }
其中,this是方法内部就有的,在这里this.age代表当第一个元素存储后的后续每一个元素的年龄,我们用后续存储的元素年龄减去第一个元素的年龄当结果是-1时,就将该元素排在第一个元素前面,为1时,就排在后面,为0时就代表重复不存储
但是在我们完成按照年龄进行排序后有出现一个问题:当两个元素姓名不同年龄相同时,再按照我们设定的规则就不会将年龄相同的最后一个元素存储进去,因为它们两个年龄相减为0,默认重复了。所以在年龄相同的情况下,我们还要再比较姓名,
如下:
public int compareTo(Student s) { //return 0; //return 1; int num=this.age-s.age; int num1=num==0?this.name.compareTo(s.name):num; return num1; }
当年龄不同时返回的还是之前num
的值,当年龄相同时比较姓名是否相同不相同返回1代表可以进行存储,相同返回0代表重复。
在这里因为string 本身就实现了comparable
接口,所以可以直接调用compareto
方法,这样就很好的解决了问题又保证了数据的唯一性
总结:
- 1.TreeSet集合存储自定义对象时,无参构造方法使用的是自然排序也就是按照存储元素的顺序进行排序
- 2.自然排序也就是让元素所属的类实现Comparable接口,重写compareto(T o)方法
- 3.重写compareto(T o)方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
比较器排序Comparator
练习:
存储学生对象并遍历,创建TreeSet
集合使用带参构造方法,并按照年龄从小到大的顺序排序,若年龄相同再按照姓名的字母顺序排序
分析:较于comparable来说,comparator
无需在Student类中实现comparable
接口,可以直接在创建TreeSet集合对象时使用内部类的方式进行
public class StudentDemo { public static void main(String[] args) { //创建TreeSet对象 TreeSet<Student>ts=new TreeSet<Student>(new Comparator<Student>() { @Override public int compare(Student s1, Student s2) { int num =s1.getAge()-s2.getAge(); int num1=num==0?s1.getName().compareTo(s2.getName()):num; return num1; } }); //创建学生对象 Student s=new Student("张三",18); Student s1=new Student("张四",17); Student s2=new Student("张五",19); Student s3=new Student("张六",12); Student s4=new Student("张七",12); //添加数据 ts.add(s); ts.add(s1); ts.add(s2); ts.add(s3); ts.add(s4); for (Student ss:ts){ System.out.println(ss.getName()+ss.getAge()); } } }
其中需要注意的是,compare方法传递了两个参数,s1就等同于compareto中的this,但由于内部类无法直接访问Student类的私有成员变量,只能通过get方式来获取,效果等同于自然排序Comparable
感谢1楼前辈给出的指导,文中出现的错误与更正如下:
1.TreeSet
集合存储自定义对象时,无参构造方法使用的是自然排序也就是按照存储元素的顺序进行排序) 正确的总结:使用TreeSet时,要么实现Comparable接口,要么指定Comparator,两者并存时优先使用Comparator。否则add方法报错cannot be cast to java.lang.Comparable
。
可以在TreeMap中找到源码:
/** * Compares two keys using the correct comparison method for this TreeMap. */ @SuppressWarnings("unchecked") final int compare(Object k1, Object k2) { return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2) : comparator.compare((K)k1, (K)k2); } // Student未实现Comparable接口时,初始化TreeSet时,需指定Comparator TreeSet<Student> ts2 = new TreeSet<>(Comparator.comparingInt(Student::getAge).thenComparing(Student::getName)); ts.add(new Student("张三", 18));
加载全部内容