新的一天,大家一起加油!
引子
我们之前的文章的文章提到了Arrays是一个数组工具类,用Arrays.sort能够对各种类型的数组进行排序,那下面的数组能不能用Arrays.sort排序呢?
class Student { // 自定义的学生类 String name; int age; public Student(String name, int age) { this.name = name; this.age = age; } @Override // 对父类Object的父类方法进行重写,以便直接打印出当前对象的值 public String toString() { // 这是IDEA的自动生成的toString重写,你也可以按照自己的喜好自由的选择打印的形式 return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } } public class test4 { public static void main(String[] args) { Student[] students = new Student[] { // 我们定义了一个数组,数组中每个元素都是一个学生对象 new Student("zhangsan", 13), new Student("lisi", 23), new Student("able", 17), }; Arrays.sort(students); // 用类Arrays.sort对students数组进行排序 System.out.println(Arrays.toString(students)); // 注意这里的toString是Arrays自己的toString方法,和Object类中的toString方法不是同一个方法, // 只是碰巧同名,参数都不一样,Arrays的有参数,Object没参数 // 我们前几篇文章专门对Arrays以及toString方法的重写做了说明,这里不再赘述 } }
但你运行的时候,你会发现程序报错了😮
对了,我记得sort只能对给定长度的数组排序,否则会发出空指针异常,但这里就是给定长度的数组啊!为啥还是错呢?
程序抛出了异常😮,你说不对呀!我的Arrays不是对任意的数组都可以进行操作吗?之前我们就用Arrays.toString成功打印了数组元素都是对象的一个数组呀!
但是朋友,你告诉Arrays拿什么排了吗?是按姓名 还是按年龄呢?
🍑所以我们要告诉编译器那什么来排序,而这就要用到我们的Comparable接口了
Comparable接口
📝通过参考手册我们可以看到
我们的程序就可以写成这样
//Comparable接口在java.lang这个包下面,而java.lang这个包由java解释器自动引入,而不用import语句引入 class Student implements Comparable<Student>{ // 实现Comparable接口,注意Comparable后要加上要比较的对象的类型<Student> String name; int age; public Student(String name, int age) { this.name = name; this.age = age; } @Override // 对父类Object的父类方法进行重写,以便直接打印出当前对象的值 public String toString() { return "[" + this.name + ":" + this.age + "]"; } @Override // 实现Comparable接口中的中的compareTo方法 public int compareTo(Student o) { if (this.age > o.age) return 1; // this.代表对当前对象的引用,o.代表对参数对的引用 else if (this.age < o.age) return -1; else return 0; } //如果当前对象应排在参数对象之前, 返回小于 0 的数字; //比如上面我们如果this.age>o.age,那么返回1,就this.age在o.age的后面,即按年龄升序排列 //如果当前对象应排在参数对象之后, 返回大于 0 的数字; //如果当前对象和参数对象不分先后, 返回 0; } public class test4 { public static void main(String[] args) { Student[] students = new Student[] { // 我们定义了一个数组,数组中每个元素都是一个学生对象 new Student("zhangsan", 13), new Student("lisi", 23), new Student("able", 17), }; Arrays.sort(students); // 用类Arrays.sort对students数组进行排序 System.out.println(Arrays.toString(students)); // 注意这里的toString是Arrays自己的toString方法,和Object类中的toString方法不是同一个方法, // 只是碰巧同名,参数都不一样,Arrays的有参数,Object没参数 // 我们前几篇文章专门对Arrays以及toString方法的重写做了说明,这里不再赘述 } }
😁看一下输出结果
📝咱们再回过头来看看为为啥必须要实现接口中的 compareTo 方法,
上图中的这个(Comparable)a[runHi++]数组就是我们Arrays.sort传过去的学生对象数组🤔,下图中的this指的是谁🤔?就要看谁此时调用了compareTo方法😎
上图中compareTo(a[lo])点号前面的的学生对象a[runHi++]就是此时的this😮。下图中的o对象就是上图中的compareTo(a[lo]中的a[lo]学生对象。
不知道你理解了吗😂?如果没理解,我们不妨再举一个栗子
🌰总结一下:
📝我们知道在Arrays.sort(students); 中是传了一个学生对象数组,如上图所示在调用Arrays对对象数组排序的时候,其实就调用了我们的Comparable接口中的compareTo方法对数组的每个元素进行了排序😁
📝其实在Java中对于引用数据类型的比较或者排序,一般都要用到使用Comparable接口中的compareTo() 方法
🍑那如何按年龄排序呢?一样的道理(只是发生了一些小小的变化):
//Comparable接口在java.lang这个包下面,而java.lang这个包由java解释器自动引入,而不用import语句引入 class Student implements Comparable<Student>{ // 实现Comparable接口,注意Comparable后要加上要比较的对象的类型<Student> String name; int age; public Student(String name, int age) { this.name = name; this.age = age; } @Override // 对父类Object的父类方法进行重写,以便直接打印出当前对象的值 public String toString() { return "[" + this.name + ":" + this.age + "]"; } @Override // 实现Comparable接口中的中的compareTo方法 public int compareTo(Student o) { // 为啥在compareTo里还要用compareTo,因为name是个String类型也是个引用类型,也需要用compareTo比较, 不过这里用到的compareTo方法是String类里重写的 if (this.name.compareTo(o.name) > 0) return 1; // this.代表对当前对象的引用,o.代表对参数对的引用 else if (this.name.compareTo(o.name) < 0) return -1; else return 0; } //如果当前对象应排在参数对象之前, 返回小于 0 的数字; //如果当前对象应排在参数对象之后, 返回大于 0 的数字; //如果当前对象和参数对象不分先后, 返回 0; } public class test4 { public static void main(String[] args) { Student[] students = new Student[] { // 我们定义了一个数组,数组中每个元素都是一个学生对象 new Student("zhangsan", 13), new Student("lisi", 23), new Student("able", 17), }; Arrays.sort(students); // 用类Arrays.sort对students数组进行排序 System.out.println(Arrays.toString(students)); // 我们前几篇文章专门对Arrays以及toString方法的重写做了说明,这里不再赘述 } }
📝但这样会有一个问题,那就是一但写好了排序的方法,那就一下子就写死了😮。你在compareTo方法里实现的是按年龄比较,那你接下来就只能按年龄来比较🤔。如果想按姓名来比较,那只能够在重写compareTo方法。
🍑那有什么办法能够解决这个问题呢?
有!那就是我们接下来要讲的比较器:Comparator接口
所以就像Comparable 接口一样,我们只要实现了Comparator接口,并重写Comparator里的compare方法就可以实现对学生对象数组的排序
🌰比如我们上面的年龄比较就可以写成这样
1.class Student { String name; int age; public Student(String name, int age) { this.name = name; this.age = age; } @Override // 对父类Object的父类方法进行重写,以便直接打印出当前对象的值 public String toString() { return "[" + this.name + ":" + this.age + "]"; } } class AgeComparator implements Comparator<Student> { // 年龄比较器 @Override // 实现Comparator接口中的compare方法 public int compare(Student o1, Student o2) { return o1.age - o2.age; // 反正返回的也是数字,当o1.age>o2.age时返回大于零的数,即o1对象排在o2对象的后面,升序排列,我们之前用Comparable接口时也可以这样简写 } //如果当前对象应排在参数对象之前, 返回小于 0 的数字; //如果当前对象应排在参数对象之后, 返回大于 0 的数字; //如果当前对象和参数对象不分先后, 返回 0; } public class test4 { public static void main(String[] args) { Student[] students = new Student[]{ // 我们定义了一个数组,数组中每个元素都是一个学生对象 new Student("zhangsan", 13), new Student("lisi", 23), new Student("able", 17), }; AgeComparator ageComparator = new AgeComparator(); // 对年龄比较器进行实例化 Arrays.sort(students, ageComparator); // 用类Arrays.sort对students数组进行排序,这里传了两个参数(学生对象和所对应的年龄比较器) System.out.println(Arrays.toString(students)); // 我们前几篇文章专门对Arrays以及toString方法的重写做了说明,这里不再赘述 } }
📝Arrays.sort此时是一个传两个参数的方法:
🌰那姓名比较比较自然就出来了
class Student { String name; int age; public Student(String name, int age) { this.name = name; this.age = age; } @Override // 对父类Object的父类方法进行重写,以便直接打印出当前对象的值 public String toString() { return "[" + this.name + ":" + this.age + "]"; } } class NameComparator implements Comparator<Student> { // 姓名比较器 @Override // 实现Comparator接口中的compare方法 public int compare(Student o1, Student o2) { return o1.name.compareTo(o2.name); // 因为name是String类型,也是一个引用类型,也要用到compareTo方法,此时的compareTo方法是String类里重写的方法 } //如果当前对象应排在参数对象之前, 返回小于 0 的数字; //如果当前对象应排在参数对象之后, 返回大于 0 的数字; //如果当前对象和参数对象不分先后, 返回 0; } public class test4 { public static void main(String[] args) { Student[] students = new Student[]{ // 我们定义了一个数组,数组中每个元素都是一个学生对象 new Student("zhangsan", 13), new Student("lisi", 23), new Student("able", 17), }; NameComparator nameComparator = new NameComparator(); Arrays.sort(students, nameComparator); // 用类Arrays.sort对students数组进行排序 System.out.println(Arrays.toString(students)); // 我们前几篇文章专门对Arrays以及toString方法的重写做了说明,这里不再赘述 } }
总结一下就是:
📝Comparable接口和Comparator接口都是Java中用来比较和排序引用类型数据的接口,要实现比较,就需要实现他们所各种对应的compareTo方法或者compare方法。
🌰不同的是:Comparator使用起来更加灵活,所以我更倾向于使用比较器:Comparator
但注意,sort只能对给定长度的数组排序,否则会发出空指针异常
好了,今天我们就聊到这里,咱们下篇博客见🥰