目录
1.前言
Java 中的对象,正常情况下,只能进行比较:== 或 != 。不能使用 > 或 < 。但是在实际开发场景中,有时候需要对多个对象进行排序,言外之意,就是需要比较对象的大小。这便引出了 Comparable、Comparator 两个接口
2.Comaprable接口
Comparable 位于 java.lang.Comaprable,其源码如下:
package java.lang;
import java.util.*;
public interface Comparable<T> {
public int compareTo(T o);
}
2.1 使用细节
① 实现 Comparable 接口的类必须实现 int compareTo(T o) 方法,两个对象可以通过 int compareTo(T o) 方法的返回值来比较大小:
(1)当前对象 this 大于形参对象 o,则返回正整数
(2)当前对象 this 小于形参对象 o,则返回负整数
(3)当前对象 this 等于形参对象 o,则返回 0
② 实现 Comparable 接口的对象列表或数组可以通过 Collections.sort 或 Arrays.sort 进行自动排序,无需指定 Collection.sort(...)、Arrays.sort(...) 方法的第二个比较器参数,底层会根据类中实现的 int compareTo(T o) 作为排序逻辑
③ String、八大包装类都 implements 了该接口,并实现了 int compareTo(T o) 方法,都是从小到大排列的
String:按照字符串中字符的 ASCII 值进行比较
Character:按照字符的 ASCII 值来进行比较
数值类型对应的包装类以及 BigInteger、BigDecimal :按照它们对应的数值大小进行比较
Boolean:true 对应的包装类实例大于 false 对应的包装类实例
Date、Time等:后面的日期时间比前面的日期时间大
2.2 案例演示
案例1 :根据学生的年龄对学生类型数组进行升序排序
import java.util.Arrays;
import java.util.Comparator;
public class demo {
public static void main(String[] args) {
Student s1 = new Student("小马", 100);
Student s2 = new Student("蔡徐坤", 5);
Student s3 = new Student("ftt", 90);
Student[] arr1 = {s1,s2,s3};
Student[] arr2 = {s1,s2,s3};
System.out.println("排序前,所有学生:");
Arrays.stream(arr1).forEach(student -> System.out.println(student));
System.out.println("----------------------");
//方式1:使用冒泡排序
System.out.println("按照年龄排序:");
for (int i = 1; i < arr1.length; i++) {
for (int j = 0; j < arr1.length-i; j++) {
if(arr1[j].compareTo(arr1[j+1])>0){
Student temp = arr1[j];
arr1[j] = arr1[j+1];
arr1[j+1] = temp;
}
}
}
Arrays.stream(arr1).forEach(student -> System.out.println(student));
System.out.println("----------------------");
//方式2:使用Arrays.sort排序,因为在Student类已实现Comparable接口,所以无需指定比较器
Arrays.sort(arr2);
Arrays.stream(arr2).forEach(student -> System.out.println(student));
}
}
class Student implements Comparable<Student> {
public String name;
public int age;
public Student(){}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
//按学生年龄升序排序
@Override
public int compareTo(Student anotherStudent) {
if(this.age > anotherStudent.age){
return 1;
}else if(this.age < anotherStudent.age){
return -1;
}else{
return 0;
}
}
}
案例1运行结果:
案例2 :对字符数组进行排序
import java.util.Arrays;
public class demo {
public static void main(String[] args) {
Character[] arr = {'b','a','c','d'};
System.out.println("排序前的字符数组:"+Arrays.toString(arr));
Arrays.sort(arr);//Arrays底层会将Character类中实现的 int compareTo(T o) 方法作为排序逻辑
System.out.println("排序后的字符数组:"+Arrays.toString(arr));
}
}
案例2运行结果:
3.Comparator接口
Comparator 接口位于 java.until.Comparator 下,其中关于 int compare(...) 接口的源码如下:
package java.util;
public interface Comparator<T>{
int compare(T o1,T o2);
}
3.1 为什么需要Comparator接口
场景 ① :当只有第三方的类时(即只有 .class 字节码文件,没有 .java 源文件,无法修改代码),且这些类没有实现 java.lang.Comparable 接口
场景 ② :类中提前实现了 Comparable 接口,指定了两个对象比较大小的规则,但此时如果不想按照它预定义的方法比较大小,但是又不能随意修改源码,因为会影响其他地方的使用,怎么办呢?
因此,基于 ①、② ,Java 增加了一个 java.util.Comparator 接口来解决问题
3.2 使用细节
① 通常是用一个单独的其他的类来实现 Comparator 接口
② 实现 Comparator 的类必须实现 int compare (T o1,T o2) 方法,两个对象可以通过 int compareTo (T o1,T o2) 方法的返回值来比较大小:
(1) o1 大于 o2,则返回正整数
(2) o1 小于 o2,则返回负整数
(3) o1 等于 o2,则返回 0
③ 在使用 Collection.sort 或 Arrays.sort 实现排序功能时,可以将实现了 Comparator 接口的类的实例传递给 Collection.sort 或 Arrays.sort,作为排序逻辑的比较器
3.3 案例演示
案例
import java.util.Arrays;
import java.util.Comparator;
public class demo {
public static void main(String[] args) {
//定义比较器实例
StudentScoreComparator sc = new StudentScoreComparator();
Student s1 = new Student("小马", 22,98);
Student s2 = new Student("jack", 55,90);
Student s3 = new Student("mary", 30,67);
Student[] arr1 = {s1,s2,s3};
Student[] arr2 = {s1,s2,s3};
System.out.println("按分数排序前,所有学生:");
Arrays.stream(arr1).forEach(student -> System.out.println(student));
System.out.println("----------------------");
//方式1:使用冒泡排序
System.out.println("按照分数排序:");
for (int i = 1; i < arr1.length; i++) {
for (int j = 0; j < arr1.length-i; j++) {
if(sc.compare(arr1[j],arr1[j+1]) > 0) {
Student temp = arr1[j];
arr1[j] = arr1[j+1];
arr1[j+1] = temp;
}
}
}
Arrays.stream(arr1).forEach(student -> System.out.println(student));
System.out.println("----------------------");
//方式2:使用Arrays.sort排序,指定StudentScoreComparator为比较器,按学生的分数排序
Arrays.sort(arr2,sc);
Arrays.stream(arr2).forEach(student -> System.out.println(student));
}
}
//定制比较器类,按学生的分数升序排序
class StudentScoreComparator implements Comparator<Student>{
@Override
public int compare(Student s1, Student s2) {
if(s1.score > s2.score){
return 1;
}else if(s1.score < s2.score){
return -1;
}else{
return 0;
}
}
}
class Student implements Comparable<Student> {
public String name;
public int age;
public int score;
public Student(){}
public Student(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
//按学生年龄升序排序
@Override
public int compareTo(Student anotherStudent) {
if(this.age > anotherStudent.age){
return 1;
}else if(this.age < anotherStudent.age){
return -1;
}else{
return 0;
}
}
}
案例1运行结果:
4.Comparable、Comparator对比
Comparable | Comparator | |
---|---|---|
实现位置 | 类内部实现 | 外部独立实现 |
排序规则数量 | 只能定义一种排序规则 | 可根据业务求定义多种排序规则 |
侵入性 | 需要修改原类代码 | 不需要修改原类代码 |
方法 | int compareTo (T o) | int compare (T o1, T o2) |
使用场景 | 默认排序,如 Arrays.sort(arr) | 灵活排序,如 Arrays.sort(arr,comparator) |