面试回答
Java.util 包中的 List 接口继承了 Collection 接口,用来存放对象集合,所以对这些对象进行排序的时候,要么让对象类自己实现同类对象的比较,要么借助比较器进行比较排序。
举例:学生实体类,包含姓名和年龄属性,比较时先按姓名升序排序,如果姓名相同则按年龄升序排序。
实现 Comparable
第一种:实体类自己实现 Comparable 接口比较。
@Data
@Builder
@ToString
public class Student implements Comparable<Student> {
private String name;
private int age;
@Override
public int compareTo(Student o) {
int flag=this.name.compareTo(o.name);
if (flag==0){
flag=this.age-o.age;
}
return flag;
}
public static void main(String[] args) {
Student stu1=Student.builder().name("bb").age(19).build();
Student stu2=Student.builder().name("aa").age(19).build();
Student stu3=Student.builder().name("bb").age(17).build();
List<Student> list= Arrays.asList(stu1,stu2,stu3);
for (Student stu:list){
System.out.println(stu);
}
System.out.println("--------------");
Collections.sort(list);
for (Student stu:list){
System.out.println(stu);
}
}
}
输出如下:
Student(name=bb, age=19)
Student(name=aa, age=19)
Student(name=bb, age=17)
--------------
Student(name=aa, age=19)
Student(name=bb, age=17)
Student(name=bb, age=19)
借助 Comparator
第二种:借助比较器进行排序。
package com.chiyi.test;
import lombok.Builder;
import lombok.Data;
import lombok.ToString;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@Data
@Builder
@ToString
public class Student {
private String name;
private int age;
public static void main(String[] args) {
Student stu1=Student.builder().name("bb").age(19).build();
Student stu2=Student.builder().name("aa").age(19).build();
Student stu3=Student.builder().name("bb").age(17).build();
List<Student> list= Arrays.asList(stu1,stu2,stu3);
for (Student stu:list){
System.out.println(stu);
}
System.out.println("--------------");
list.sort(Comparator.comparing(Student::getName)
.thenComparingInt(Student::getAge));
for (Student stu:list){
System.out.println(stu);
}
}
}
借助 Stream
第三种:借助 Stream 进行排序,借助 Stream 的 API,底层还是通过 Comparable 实现的。
@Data
@Builder
@ToString
public class Student {
private String name;
private int age;
public static void main(String[] args) {
Student stu1 = Student.builder().name("bb").age(19).build();
Student stu2 = Student.builder().name("aa").age(19).build();
Student stu3 = Student.builder().name("bb").age(17).build();
List<Student> list = Arrays.asList(stu1, stu2, stu3);
for (Student stu : list) {
System.out.println(stu);
}
System.out.println("--------------");
List<Student> list2 = list.stream().sorted((Comparator.comparing(Student::getName)
.thenComparingInt(Student::getAge))).collect(Collectors.toList());
for (Student stu : list2) {
System.out.println(stu);
}
}
}
知识扩展
有了 Comparable 为什么还需要 Comparator?
Comparable 用于使某个类具备可排序能力。如之前的 Student 类,实现该接口后覆盖其 compareTo 方法,即可具备可排序的能力。
但是仍然存在一些二方库的类没有实现 Comparable,但是调用方法也需要比较的,此时就需要使用 Comparator 接口。
Comparator 是一个比较器接口,可以用来给不具备排序能力的对象进行排序。如上述代码中对不具备排序能力的 Student 进行排序。
compareTo 和 equals 的使用场景有何区别?
compareTo 常用于排序和 BigDecimal 等数值的比较。
equals 则是常用于业务语义中两个对象是否相同,如 String 常常通过 equals 来比较是否字面意义相同。
既然 Set 是无序的,还怎么排序?
这里说的是两个语境的不同,Set 的无序,指的是插入顺序是无序的。虽然 Set 的插入顺序是无序的,Set 也可以基于 SortedSet 要求对象实现 Comparable 来对 Set 中的元素进行排序。
Set 真的是插入无序的吗?
并不是,Set 有一个实现类是 LinkedHashSet,它引入了 LinkedHashMap,通过双向链表记录了每个 node 的插入顺序和查询顺序(可选),以此来达到 Set 的插入有序性。