文章目录
前言
对集合中的元素排序,我们可以使用 Collections 工具类如:
List<Integer> list1 = new ArrayList<>();
list1.add(2);
list1.add(1);
list1.add(5);
System.out.println(list1);
Collections.sort(list1);
System.out.println(list1);
// 输出
[2, 1, 5]
[1, 2, 5]
为什么是升序排序?
怎么改为降序呢?
如果想要对一个学生的属性进行排序?如:
class Student {
public String name;
public int age;
public double score;
public Student(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
}
public class TestDemo {
public static void main(String[] args) {
List<Student> list = new ArrayList<>();
list.add(new Student("张三", 12, 70));
list.add(new Student("李四", 18, 81));
list.add(new Student("王五", 21, 93));
System.out.println(list);
// 显然这里是不行的
Collections.sort(list);
System.out.println(list);
}
}
显然这里是不行的,谁知道这是按学生的哪个属性排序,那么怎么去自定义排序的方式呢?
1.这里为什么是升序排序?
先说结论:Integer 包装类 实现了Comparable 接口,重写了 compareTo 方法,而 compareTo 方法的返回值影响排序是升序还是降序
1.1 我们来看看 JDK1.8 的源码
Collections.sort 源码:
我们这里的 List 保存的是 Integer 类型,源码这里可知 Integer 一定实现了Comparable 接口
我们看看 Integer包装类 确实继承了Comparable 接口
1.2 Comparable 接口
再看看 Comparable 接口,接口中只有一个 compareTo 的抽象方法, 返回一个 int 类型的值
Integer 包装类中重写 compareTo 方法,compareTo 方法中调用了 compare 方法
compare 方法返回的 0 或者 -1 或者 1 取决于传入两数的大小,在 x < y返回 -1,x > y 返回1,x = y 返回 0;
1.3 Comparable 接口中的 compareTo 方法和排序有什么关系?
sort 传入null
这里圈起来的都为 null
点进这里
继续
终于破案了,我们的 list 一路传递到这里,在这里调用了 刚才的 compareTo 方法,这里就是排序的部分代码,compareTo 方法的返回值影响排序是升序还是降序如果前一个数大于后一个数,返回1,交换顺序,升序排序。
2.怎么改为降序呢?
由上边的分析我们可知只需修改 compareTo 的比较方式就行,但是这里的 compareTo 方法是 Integer 包装类中重写的,我们没办法修改。那让我们再看看另外一个 sort 的重载方法,发现还有第二个参数
2.1 Comparator 接口
Comparator 接口中有一个 compare的抽象方法,也是返回一个 int 类型
2.2 Comparator 接口中的 compare 方法和排序有什么关系?
这次传入的第二个参数不为 null,进入这里
最后同 compareTo 一样,在最后 compare 比较返回值影响排序的结果
2.3 实现 Comparator 接口,改为降序排序
不会写没关系,直接复制刚才 Integer 里的代码,修改 (x < y) ? -1 : ((x == y) ? 0 : 1); 修改为
(x > y) ? -1 : ((x == y) ? 0 : 1),相当于调换了 1 和 -1 的返回
class MyCompare implements Comparator<Integer> {
@Override
public int compare(Integer x, Integer y) {
return (x > y) ? -1 : ((x == y) ? 0 : 1);
}
}
public class TestDemo {
public static void main(String[] args) {
List<Integer> list1 = new ArrayList<>();
list1.add(2);
list1.add(1);
list1.add(5);
System.out.println(list1);
MyCompare myCompare = new MyCompare();
Collections.sort(list1, myCompare);
System.out.println(list1);
}
}
// 输出:
[2, 1, 5]
[5, 2, 1]
我们还可以将 compare 方法修改为这样,这是相同的意思,而且更简便。
class MyCompare implements Comparator<Integer> {
@Override
public int compare(Integer x, Integer y) {
return y - x;
}
}
2.4 使用匿名内部类和 lambda 表达式
以下几种方式都可以实现降序排序,为方便记忆,o1 作为第一个参数, o2 作为第二个参数,o2 - o1 降序(2 ,1),o1 - o2 升序(1,2)
Collections.sort(list1, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
// lambda 表达式
Collections.sort(list1, (Integer o1, Integer o2) -> {return o2 - o1; });
// 简写
Collections.sort(list1, (o2, o1) -> o2 - o1);
// Collections.sort 也是调用了list.sort,所以也可以直接这样写
list1.sort((o2, o1) -> o2 - o1);
3.对一个学生的属性进行排序
3.1 Student 类实现 Comparable 接口
因为 Student 是我们写的类,那么就可以学这 Integer 包装类 去实现 Comparable 接口,先看看刚才 Integer 是怎么写的
this.value 作为第一个参数,compareTo 传入的参数作为第二个参数
我们也可以理解为
public int compareTo(Integer o) {
return this.value- o.value;
}
自己实现
class Student implements Comparable<Student>{
public String name;
public int age;
public double score;
public Student(String name, int age, double 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 o) {
return this.age - o.age;
}
}
public class TestDemo {
public static void main(String[] args) {
List<Student> list = new ArrayList<>();
list.add(new Student("张三", 12, 70));
list.add(new Student("李四", 21, 81));
list.add(new Student("王五", 18, 93));
System.out.println(list);
Collections.sort(list);
System.out.println(list);
}
}
// 运行结果,按年龄升序排序
[Student{name='张三', age=12, score=70.0}, Student{name='李四', age=21, score=81.0}, Student{name='王五', age=18, score=93.0}]
[Student{name='张三', age=12, score=70.0}, Student{name='王五', age=18, score=93.0}, Student{name='李四', age=21, score=81.0}]
这里我们看出来要修改比较的东西时,就可能要重新修改重写的方法,而正常情况下Student 是封装好的,所以这个局限性就比较大,这才有了 Comparator 接口,也称之为比较器;
而刚才在创建类时 就实现 Comparable 接口 定义的排序方式称自然排序或内部排序。
3.2 基于 Comparator 接口,自定义比较器
public class TestDemo {
public static void main(String[] args) {
List<Student> list = new ArrayList<>();
list.add(new Student("张三", 12, 70));
list.add(new Student("李四", 21, 81));
**加粗样式** list.add(new Student("王五", 18, 93));
System.out.println(list);
// 自然排序
Collections.sort(list);
System.out.println(list);
// 自定义比较器
Collections.sort(list, (o1, o2) -> (int) (o2.score - o1.score));
System.out.println(list);
}
}
4.总结
Java 集合框架 Collections 工具类中的 sort 方法可以对集合中元素排序,前提是对象是可比较的,我们可以通过创建对象时实现Comparable 接口,或者在调用 sort 方法时将自己实现的 Comparator(比较器)接口作为参数传入。
4.1 Comparable 接口 和 Comparator 接口比较
-
Comparable 接口在创建类时要手动实现并重写 compareTo 方法,一旦实现,每次用该类都有指定的顺序,属于内部顺序。如果要更换比较的方式,则要修改 compareTo 方法,侵入性强。
-
Comparator(比较器)接口实现后作为参数传入Collections.sort。每次使用都要确定比较器,比较方式在实现比较器的时候重写 compare 方法确定,侵入性较弱。