写点东西《Java中的排序-避坑》

发布时间:2024年01月24日

关于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<Car> 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<Car> {
    ....
    @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<Car>{
        @Override
        public int compare(Car o1, Car o2) {
            return o1.yearOfProduction - o2.yearOfProduction;
        }
}
static class HorsePowerComparator implements Comparator<Car>{
        @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<Car> sorted = new ArrayList<>();
cars.stream().sorted((car1, car2) -> car1.yearOfProduction - car2.yearOfProduction)
             .forEach(car -> sorted.add(car));

第二个例子展示了使用已经实现的类YearComparator:

List<Car> sorted = new ArrayList<>();
cars.stream().sorted(new YearComparator())
             .forEach(car -> sorted.add(car));  

这两个例子看起来比第1点和第2点中的例子更清晰,但其中有一个问题-可变性!如果我们决定切换到并发迭代,我们将被迫处理线程安全问题。

如何正确执行排序这一天

1.使用Java 8流和collect方法

本文第3点中显示的所有示例都可以通过使用收集终端方法来修复。让我们改变最后一个使用’new YearComparator()'比较器的例子:

List<Car> sorted = cars.stream()
                .sorted(new YearComparator())
                .collect(Collectors.toList());

这将解决我们的并发修改问题,我们不再修改现有的对象。

2.使用Comparator的函数式风格

我们可以使用Java 8中引入的函数式编程风格来提供它的实现,而不是实现像YearComparator这样的小类。这可能看起来像这样:

Comparator<Car> years = (car1, car2) -> car1.yearOfProduction - car2.yearOfProduction;

使用此比较器的示例如下所示:

List<Car> 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<Car, Integer> year = car -> car.yearOfProduction;

然后我们就这么简单地使用它:

List<Car> sorted = cars.stream()
                .sorted(Comparator.comparing(year))
                .collect(Collectors.toList());

这将给我们给予我们想要的结果。我们甚至可以更进一步,定义第二个lambda来使用马力进行排序,并将这两个排序联合收割机结合在一起!

Function<Car, Integer> horsePower = car -> car.horsePower;

首先按年份排序,然后按马力排序,看起来像这样:

List<Car> sorted = cars.stream()        
     .sorted(Comparator.comparing(year)
     .thenComparing(horsePower))
     .collect(Collectors.toList());

结论

我们可以通过多种方式对数据进行排序。我自己发现使用Java 8 API有很多优点:

  • 非可变数据-我们从不改变我们正在排序的数据,总是创建新的结果。
  • 不强制改变我们正在排序的类(例如,实现Comparable接口)。
  • 提供简单易读的lambda定义。
  • 联合收割机几种分拣方式。

🌟更多精彩

点击👉这里~~

文章来源:https://blog.csdn.net/l316278750/article/details/135827613
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。