关于java中排序的评论和文章在互联网上有很多,这篇文章将是我在我的开发者载体中看到的例子的总结。它不会涵盖所有的基础知识,但会尝试向您展示一些可能性,从我目前试图避免的可能性到我现在更喜欢使用的可能性。
对于所有测试目的,我们将使用Car类
package com.developersmill.sorting.interfaceimpl;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public final class Car{
private final int yearOfProduction;
private final int horsePower;
private final String brand;
private final String model;
public Car(int yearOfProduction, int horsePower,
String brand, String model) {
this.yearOfProduction = yearOfProduction;
this.horsePower = horsePower;
this.brand = brand;
this.model = model;
}
@Override
public String toString() {
return "Car{" +
"yearOfProduction=" + yearOfProduction +
", horsePower=" + horsePower +
", brand='" + brand + '\'' +
", model='" + model + '\'' +
'}';
}
}
这个示例列表:
final List cars = Arrays.asList(
new Car(1989, 60,"Toyota", "Yaris"),
new Car(2010, 90,"Mazda", "3"),
new Car(2004, 110,"Toyota", "Corolla"),
new Car(1999, 150,"BMW", "5"),
new Car(2010, 60,"Renault", "Clio"),
new Car(2016, 70,"Renault", "Twingo"),
new Car(2021, 190,"Skoda", "Superb"));
1.实现Comparable接口
第一个解决方案,也是我知道的最古老的一个,是实现一个接口Comparable,并在我们想要排序的类中提供compareTo方法的实现。我们会在课上讲汽车.
public final class Car implements Comparable {
....
@Override
public int compareTo(Car car) {
return yearOfProduction - car.yearOfProduction;
}
}
要测试我们的解决方案,只需在Collections类上调用sort方法
Collections.sort(cars);
cars.forEach(System.out::println);
结果是:
Car{yearOfProduction=1989, horsePower=60, brand='Toyota', model='Yaris'}
Car{yearOfProduction=1999, horsePower=150, brand='BMW', model='5'}
Car{yearOfProduction=2004, horsePower=110, brand='Toyota', model='Corolla'}
Car{yearOfProduction=2010, horsePower=90, brand='Mazda', model='3'}
Car{yearOfProduction=2010, horsePower=60, brand='Renault', model='Clio'}
Car{yearOfProduction=2016, horsePower=70, brand='Renault', model='Twingo'}
Car{yearOfProduction=2021, horsePower=190, brand='Skoda', model='Superb'}
该解决方案具有两个主要问题:
2.用新类提供比较器的不同实现
下一个解决方案是提供新的类来实现Comparator接口,每次我们想要改变数据的排序方式时。
例如,我们希望使用生产年份进行一次排序,使用马力进行一次排序。我们必须实现两个类:
static class YearComparator implements Comparator{
@Override
public int compare(Car o1, Car o2) {
return o1.yearOfProduction - o2.yearOfProduction;
}
}
static class HorsePowerComparator implements Comparator{
@Override
public int compare(Car o1, Car o2) {
return o1.horsePower - o2.horsePower;
}
}
每个类实现Comparator类并提供compare方法的实现。Car类接口Comparator不再需要了。现在测试我们的代码:
Collections.sort(cars, new YearComparator()); // sort data using the year of production like in the first example
Collections.sort(cars, new HorsePowerComparator()); // new way of sorting
cars.forEach(System.out::println);
它也可以直接使用汽车列表本身:
cars.sort(new HorsePowerComparator());
结果是:
Car{yearOfProduction=1989, horsePower=60, brand='Toyota', model='Yaris'}
Car{yearOfProduction=2010, horsePower=60, brand='Renault', model='Clio'}
Car{yearOfProduction=2016, horsePower=70, brand='Renault', model='Twingo'}
Car{yearOfProduction=2010, horsePower=90, brand='Mazda', model='3'}
Car{yearOfProduction=2004, horsePower=110, brand='Toyota', model='Corolla'}
Car{yearOfProduction=1999, horsePower=150, brand='BMW', model='5'}
Car{yearOfProduction=2021, horsePower=190, brand='Skoda', model='Superb'}
这个解决方案要好得多,但仍不完美。类不必实现接口,所以我们根本不必改变它。我们只需要为我们想要使用的每种情况提供Comparator类的实现。
3.使用Java流和可变列表
下一个示例显示,使用Java 8 API也可以很容易地完成排序。
我们的目标还是按照生产年份对汽车列表进行排序。为此,我们将使用带有Comparator参数的sorted方法。这可以通过几种方式来实现。下面的第一个例子展示了Comparator的内联实现,代码是一个lambda表达式。
List sorted = new ArrayList<>();
cars.stream().sorted((car1, car2) -> car1.yearOfProduction - car2.yearOfProduction)
.forEach(car -> sorted.add(car));
第二个例子展示了使用已经实现的类YearComparator:
List sorted = new ArrayList<>();
cars.stream().sorted(new YearComparator())
.forEach(car -> sorted.add(car));
这两个例子看起来比第1点和第2点中的例子更清晰,但其中有一个问题-可变性!如果我们决定切换到并发迭代,我们将被迫处理线程安全问题。
1.使用Java 8流和collect方法
本文第3点中显示的所有示例都可以通过使用收集终端方法来修复。让我们改变最后一个使用’new YearComparator()'比较器的例子:
List sorted = cars.stream()
.sorted(new YearComparator())
.collect(Collectors.toList());
这将解决我们的并发修改问题,我们不再修改现有的对象。
2.使用Comparator的函数式风格
我们可以使用Java 8中引入的函数式编程风格来提供它的实现,而不是实现像YearComparator这样的小类。这可能看起来像这样:
Comparator years = (car1, car2) -> car1.yearOfProduction - car2.yearOfProduction;
使用此比较器的示例如下所示:
List sorted = cars.stream().sorted(years)
.collect(Collectors.toList());
最终结果看起来像这样:
Car{yearOfProduction=1989, horsePower=60, brand='Toyota', model='Yaris'}
Car{yearOfProduction=1999, horsePower=150, brand='BMW', model='5'}
Car{yearOfProduction=2004, horsePower=110, brand='Toyota', model='Corolla'}
Car{yearOfProduction=2010, horsePower=60, brand='Renault', model='Clio'}
Car{yearOfProduction=2010, horsePower=90, brand='Mazda', model='3'}
Car{yearOfProduction=2016, horsePower=70, brand='Renault', model='Twingo'}
Car{yearOfProduction=2021, horsePower=190, brand='Skoda', model='Superb'}
3.函数式风格与函数和比较
当Java 8引入时,Comparator接口充满了大量的静态方法。其中之一就是比较。它允许使用Function方法作为参数来使用其逻辑,以创建新的Comparator。最好是用一个例子来展示它。
假设我们有一个简单的Car POJO类,没有实现Comparable接口。我们想再次对年份属性进行排序。我们定义了我们的lambda,来说明我们想要执行排序的参数:
Function year = car -> car.yearOfProduction;
然后我们就这么简单地使用它:
List sorted = cars.stream()
.sorted(Comparator.comparing(year))
.collect(Collectors.toList());
这将给我们给予我们想要的结果。我们甚至可以更进一步,定义第二个lambda来使用马力进行排序,并将这两个排序联合收割机结合在一起!
Function horsePower = car -> car.horsePower;
首先按年份排序,然后按马力排序,看起来像这样:
List sorted = cars.stream()
.sorted(Comparator.comparing(year)
.thenComparing(horsePower))
.collect(Collectors.toList());
结论
我们可以通过多种方式对数据进行排序。我自己发现使用Java 8 API有很多优点:
点击这里~~