Java中的Comparator比较器——基础篇
概述
本文介绍Java中Comparator比较器的基础用法,包括其作用、核心方法以及不同的使用方式。
示例代码
package com.example.myspringboot.comparator;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Student> students = Arrays.asList(
new Student("Alice", 22, 178),
new Student("Bob", 20, 180),
new Student("Charlie", 23, 190),
new Student("David", 20, 175),
new Student("Alan", 20, 175)
);
// Java 8 之前的排序方式
// Comparator<Student> ageComparator = new Comparator<Student>() {
// @Override
// public int compare(Student s1, Student s2) {
// return s1.getAge().compareTo(s2.getAge());
// }
// };
// Java 8 Lambda表达式
// students.sort((Student o1,Student o2)->{return o1.getAge().compareTo(o2.getAge());});
// 简化类型
// students.sort((o1, o2)->{return o1.getAge().compareTo(o2.getAge());});
// 如果 Lambda 的方法体只有一行代码,那么大括号 {} 和 return 关键字都可以省略。
// students.sort((o1,o2)->o1.getAge().compareTo(o2.getAge()));
// Java 8 方法引用
// students.sort(Comparator.comparing(Student::getAge));
// System.out.println("按年龄升序排序");
// for (Student student : students) {
// System.out.println(student);
// }
// 多条件排序
// students.sort(
// // 先按照年龄升序
// Comparator.comparing(Student::getAge)
// // 年龄相同的情况下,按照身高降序
// .thenComparing(Student::getHeight, Comparator.reverseOrder())
// // 年龄和身高都相同的情况下,按照姓名升序
// .thenComparing(Student::getName)
// );
// System.out.println("多条件排序");
// for (Student student : students) {
// System.out.println(student);
// }
Collections.sort(students);
for (Student student : students) {
System.out.println(student);
}
}
@Data
@AllArgsConstructor
static class Student implements Comparable<Student> {
private String name;
private Integer age;
private Integer height;
@Override
public int compareTo(Student o) {
// 先按照年龄升序
int compareTo1 = this.age.compareTo(o.age);
if (compareTo1 == 0) {
// 年龄相同的情况下,按照身高降序
int compareTo2 = o.height.compareTo(this.height);
if (compareTo2 == 0) {
// 年龄和身高都相同的情况下,按照姓名升序
return this.name.compareTo(o.name);
}
return compareTo2;
}
return compareTo1;
}
}
}
Comparator比较器的作用
Comparator是Java中的一个常用接口,主要用于排序场景。Comparator比较器的作用是告诉排序算法,两个对象的相对位置关系。排序的本质就是确定两个对象,谁排在前,谁排在后,而Comparator比较器正是来达成这个目的。
为什么需要一个专门的比较器来确定两个对象的位置关系呢?对于字符串、整数等常见的数据类型,我们可以根据字典序、数字自然顺序来进行排序,但是现实世界中,需要排序的对象也包含复杂的业务对象。对于排序算法(例如快速排序、冒泡排序)来说,它们无法直接理解这些复杂对象的内部结构,例如对学员的年龄属性排序,排序算法是无法知道每一个学员的年龄属性是如何获取的。另外,需要升序?还是降序?这些排序算法都是无法知道这完全取决于程序员的意图。 那么排序算法所不知道的这些,我们该如何告知它呢?答案就是通过Comparator比较器来实现“排序意图传递”。
Comparator排序器需要传递哪些意图给排序算法呢?
- 需要对哪个属性进行排序,如何获取它们;
- 升序还是降序排列。例如students.sort((o1,o2)-> o2.getAge().compareTo(o1.getAge()))通过getAge表明需要根据对象的age属性进行排序,并且o2.getAge().compareTo(o1.getAge())返回值符号决定了最终是降序排列。
比较器的核心方法compare(o1,o2)
@FunctionalInterface
public interface Comparator<T> {
/**
* 通过比较两个参数的顺序,根据第一个参数小于、等于、大于第二个参数,
* 分别返回一个负数、零、正数
*
* @param o1 要比较的第一个对象
* @param o2 要比较的第二个对象
* @return 根据第一个参数小于、等于或大于第二个参数,分别返回一个负整数、零或正整数。
* @throws NullPointerException if an argument is null and this
* comparator does not permit null arguments
* @throws ClassCastException if the arguments' types prevent them from
* being compared by this comparator.
*/
int compare(T o1, T o2);
}
通过查阅JDK的接口文档,我们知道compare(o1,o2)的作用,其核心作用就是通过返回负数、0、正数来告诉排序算法两个对象的相对顺序。
另外,通过代码我们知道Comparator接口是一个函数式接口(有注解@FunctionalInterface),那么在使用的时候我们就可以通过lambda表达式来对其进行简洁、优雅的实现。
如何优雅的使用Comparator比较器
1. Java 8之前:匿名内部类
// Java 8 之前的排序方式
Comparator<Student> ageComparator = new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
return s1.getAge().compareTo(s2.getAge());
}
};
students.sort(ageComparator);
2. Java 8:Lambda表达式
// Java 8 Lambda表达式
students.sort((Student o1,Student o2)->{return o1.getAge().compareTo(o2.getAge());});
// 省略类型
students.sort((o1, o2)->{return o1.getAge().compareTo(o2.getAge());});
// 如果 Lambda 的方法体只有一行代码,那么大括号 {} 和 return 关键字都可以省略
students.sort((o1,o2)->o1.getAge().compareTo(o2.getAge()));
3. Java 8:方法引用
students.sort(Comparator.comparing(student -> student.getAge()));
// 用方法引用 :: 来进一步简化:
students.sort(Comparator.comparing(Student::getAge));
总结
Comparator的主要作用是告诉排序算法两个对象的相对位置关系,通过核心方法compare(o1, o2)返回负数、0、正数来确定排序顺序。
Comparator有三种使用方式:
- 匿名内部类:Java 8之前的传统方式
- Lambda表达式:Java 8引入的简洁写法
- 方法引用:最简洁优雅的实现方式
其中方法引用Comparator.comparing(Student::getAge)是推荐的写法,代码简洁且易于理解。