目录
引言
在Java中,我们使用>、< 、>=、<=等比较运算符来对基本数据类型进行比较。如果我们想要对对象数组进行排序,即比较不同对象的大小,我们需要用到Java比较器。Java实现对象排序的方式有两种:
- 自然排序:java.lang.Comparable
- 定制排序:java.util.Comparator
String对象的比较
String类型的对象数组可以直接使用Arrays.sort()进行排序,因为String类实现了Comparable接口,重写了compareTo()方法。两个字符串比较大小可以使用compareTo()方法。
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
String[] s = new String[]{"aa","cc","mm","gg","bb"};
Arrays.sort(s);
System.out.println(Arrays.toString(s)); //[aa, bb, cc, gg, mm]
System.out.println("yy".compareTo("xy")); //1
System.out.println("yy".compareTo("yy")); //0
System.out.println("xxx".compareTo("xy")); //-1
}
}
对于自定义类来说,如果需要排序,我们可以用相同的方法,即让自定义类实现Comparable接口,重写compareTo()方法。
问题一
需求:自定义一个学生类,属性包含年龄,身高,姓名。按照年龄,身高,姓名的顺序对一个学生数组进行从小到大的排序。
思路:我们可以让Student类实现Comparable接口,并重写compareTo()方法。
Comparable接口
重写compareTo(Object obj)的规则
从小到大排时:
- 如果当前对象this大于形参对象obj,则返回正整数(一般是1)。
- 如果当前对象this等于形参对象obj,则返回0。
- 如果当前对象this小于形参对象obj,则返回负整数(一般是-1)。
从大到小排时相反。
具体实现
首先创建Student类,实现Comparable接口并重写compareTo()方法。
//Student.java
public class Student implements Comparable {
//这里为了方便,不将属性设置为private,省写set和get方法
int age;
int height;
String name;
Student(int age, int height, String name) {
this.age = age;
this.height = height;
this.name = name;
}
@Override
public String toString() {
return "Student{" + "age=" + age + ", height=" + height + ", name=\"" + name +"\"}";
}
@Override
public int compareTo(Object o) {
if (this == o) return 0;
if (o instanceof Student) {
Student s = (Student)o;
//先比较age
if (this.age > s.age) {
return 1;
} else if (this.age < s.age) {
return -1;
} else {
//age相同时比较height
if (this.height > s.height) {
return 1;
} else if (this.height < s.height) {
return -1;
} else {
//height也相同时比较name
return this.name.compareTo(s.name);
}
}
}
throw new RuntimeException("传入的类型不是Student,无法比较");
}
}
创建几个学生对象测试
//Main.java
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
Student[] s = new Student[5];
s[0] = new Student(12,140,"wang");
s[1] = new Student(10,145,"zhao");
s[2] = new Student(15,142,"liu");
s[3] = new Student(12,140,"qian");
s[4] = new Student(12,138,"sun");
Arrays.sort(s);
System.out.println(Arrays.toString(s));
//排序结果
//[Student{age=10, height=145, name="zhao"},
// Student{age=12, height=138, name="sun"},
// Student{age=12, height=140, name="qian"},
// Student{age=12, height=140, name="wang"},
// Student{age=15, height=142, name="liu"}]
}
}
问题二
当有以下两种场景,Comparable接口不适用:
- 当对象所在的类中没有实现Comparable接口,并且又不方便修改这个类的代码。
- 当对象所在类中实现了Comparable接口,但是它所定义的排序规则不是你想要的。比如String类中已经实现了Comparable接口,重写了compareTo()方法,但是它所定义的是字符串从小到大排列。如果我现在想让字符串从大到小排列呢?
这时我们就需要用到Comparator接口,去实现compare()方法。
Comparator接口
重写compare(Object o1, Object o2)方法规则
从小到大排时:
- 如果o1大于o2,返回正整数。
- 如果o1等于o2,返回负整数。
- 如果o1小于o2,返回负整数。
从大到小排时相反。
实现一
让字符串从大到小排列。
//Main.java
import java.util.Arrays;
import java.util.Comparator;
public class Main {
public static void main(String[] args) {
String[] s = new String[]{"aa", "cc", "mm", "gg", "bb"};
Arrays.sort(s, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return -o1.compareTo(o2);
}
});
System.out.println(Arrays.toString(s)); //[mm, gg, cc, bb, aa]
}
}
实现二
不改变Student.java的情况下,将学生按照年龄,身高,名字降序排列。
//Main.java
import java.util.Arrays;
import java.util.Comparator;
public class Main {
public static void main(String[] args) {
Student[] s = new Student[5];
s[0] = new Student(12,140,"wang");
s[1] = new Student(10,145,"zhao");
s[2] = new Student(15,142,"liu");
s[3] = new Student(12,140,"qian");
s[4] = new Student(12,138,"sun");
Arrays.sort(s, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
if (o1 == o2) return 0;
if (o1.age > o2.age) {
return -1;
} else if (o1.age < o2.age) {
return 1;
} else {
if (o1.height > o2.height) {
return -1;
} else if (o1.height < o2.height) {
return 1;
} else {
return -o1.name.compareTo(o2.name);
}
}
}
});
System.out.println(Arrays.toString(s));
//排序结果
//[Student{age=15, height=142, name="liu"},
// Student{age=12, height=140, name="wang"},
// Student{age=12, height=140, name="qian"},
// Student{age=12, height=138, name="sun"},
// Student{age=10, height=145, name="zhao"}]
}
}
总结
- Comparable接口的方式一旦一定,保证Comparable接口的实现类的对象在任何位置都可以比较大小。
- Comparator接口相当于临时性的比较。
参考资料
[1] 宋红康.Java比较器概述.哔哩哔哩
[2] 宋红康.Comparable自然排序举例.哔哩哔哩
[3] 宋红康.自定义类实现Comparable自然排序.哔哩哔哩
[4] 宋红康.使用Comparator实现定制排序.哔哩哔哩