大家好,我是小鱼儿
新的一天,大家一起加油!
目录
引子
Comparable接口
Comparator接口
我们之前的文章的文章提到了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));
// 我们前几篇文章专门对Arrays以及toString方法的重写做了说明,这里不再赘述
}
}
但你运行的时候,你会发现程序报错了
程序抛出了异常,你说不对呀!我的Arrays不是对任意的数组都可以进行操作吗?之前我们就用Arrays.toString成功打印了数组元素都是对象的一个数组呀!
但是朋友,你告诉Arrays拿什么排了吗?是按姓名 还是按年龄呢?
所以我们要告诉编译器那什么来排序,而这就要用到我们的Comparable接口了
通过参考手册我们可以看到
我们的程序就可以写成这样
//Comparable接口在java.lang这个包下面,而java.lang这个包由java解释器自动引入,而不用import语句引入
class Student implements Comparable{ // 实现Comparable接口,注意Comparable后要加上要比较的对象的类型
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));
// 我们前几篇文章专门对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{ // 实现Comparable接口,注意Comparable后要加上要比较的对象的类型
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方法的重写做了说明,这里不再赘述
}
}
bi
但这样会有一个问题,那就是一但写好了排序的方法,那就一下子就写死了。你在compareTo方法里实现的是按年龄比较,那你接下来就只能按年龄来比较。如果想按姓名来比较,那只能够在重写compareTo方法。
那有什么办法能够解决这个问题呢?
有!那就是我们接下来要讲的比较器:Comparator接口
老规矩我们先来了解一下Comparator这个接口
所以就像Comparable 接口一样,我们只要实现了Comparator接口,并重写Comparator里的compare方法就可以实现对学生对象数组的排序
比如我们上面的年龄比较就可以写成这样
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 { // 年龄比较器
@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 { // 姓名比较器
@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
好了,今天我们就聊到这里,咱们下篇博客见