Java 8 进阶手册(XX):使用 Comparator 对列表进行排序

Posted mickjoust

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 8 进阶手册(XX):使用 Comparator 对列表进行排序相关的知识,希望对你有一定的参考价值。

目录

  1. 按字母顺序排序
  2. 对整数排序
  3. 按字符串字段排序
  4. 按双字段排序
  5. 使用自定义比较器排序
  6. .使用比较器链排序
  7. 小结

1.按字母顺序排序

List<String> cities = Arrays.asList(
              "mickjoust",
              "chengdu",
              "sichuan",
              "china",
              "java8"
      );
System.out.println(cities);
//打印:[mickjoust, chengdu, sichuan, China, Java8]

cities.sort(String.CASE_INSENSITIVE_ORDER);
System.out.println(cities);
//打印:[chengdu, China, Java8, mickjoust, sichuan]

cities.sort(Comparator.naturalOrder());
System.out.println(cities);
//打印:[China, Java8, chengdu, mickjoust, sichuan]

我写了一个小写的“ C”,以更好地突出Comparator.naturalOrder()和String.CASE_INSENSITIVE_ORDER之间的区别,Comparator.naturalOrder()返回一个以大写字母开头的 Comparator,String则不区分大小写。

在Java 7中,我们习惯先使用Collections.sort() 来排序,先是List,再是Comparator;而在Java 8中,我们有了一个新的方法:List.sort(),它接受一个Comparator。

2.对整数排序

List<Integer> numbers = Arrays.asList(6, 2, 1, 4, 9);
System.out.println(numbers); 
//[6, 2, 1, 4, 9]

numbers.sort(Comparator.naturalOrder());
System.out.println(numbers); 
//[1, 2, 4, 6, 9]

这个很容易理解,数字的先后顺序。

3.按字符串字段排序

假设,我们有Movie类,并且想按照标题对列表 List 进行排序。 在本示例中,我们可以使用Comparator.comparing()并传递一个函数,该函数提取用于排序的:字段-标题。

@Getter
@Setter
@AllArgsConstructor
@ToString
static class Movie
  private String title;

List<Movie> movies = Arrays.asList(
              new Movie("《指环王》"),
              new Movie("《肖申克的救赎》"),
              new Movie("《星球大战》"),
              new Movie("《我和我的祖国》"));
movies.sort(Comparator.comparing(Movie::getTitle));
movies.forEach(System.out::println);

打印是:

Java8Pro.Movie(title=《我和我的祖国》)
Java8Pro.Movie(title=《指环王》)
Java8Pro.Movie(title=《星球大战》)
Java8Pro.Movie(title=《肖申克的救赎》)

你可能已经注意到,我们没有通过比较器 Comparator ,但是列表已正确排序。 这是因为标题(提取的字段)是一个字符串,并且字符串实现了Comparable接口。 如果您查看Comparator.comparing()实现,将会看到它在apply时其实调用了compareTo。

4.按双字段排序

以类似的方式,我们可以使用Comparator.comparingDouble()来比较double值。 在示例中,我们要按等级从最高到最低排序电影列表。

List<Movie> movies = Arrays.asList(
        new Movie("《指环王》", 8.8),
        new Movie("《肖申克的救赎》", 9.1),
        new Movie("《星球大战》)", 8.0),
        new Movie("《我和我的祖国》", 10.0));
movies.sort(Comparator.comparingDouble(Movie::getRating).reversed());
movies.forEach(System.out::println);

打印是:

Java8Pro.Movie(title=《我和我的祖国》, rating=10.0)
Java8Pro.Movie(title=《肖申克的救赎》, rating=9.1)
Java8Pro.Movie(title=《指环王》, rating=8.8)
Java8Pro.Movie(title=《星球大战》), rating=8.0)

为了不使用默认的自然顺序,我们在Comparator上用了reverse反转函数。 即从最高到最低。 Comparator.comparingDouble()在后台使用Double.compare()。

如果需要比较int或long,则可以分别使用compareInt()和compareingLong()。

5.使用自定义比较器排序

在前面的示例中,我们没有指定任何比较器,但是我们想要自定义比较器的。例如,我们的Movie类具有一个新字段——“stard”,该字段使用第三个构造函数参数设置。 在代码示例中,我们要对列表进行排序,以便在列表顶部为已加星标的电影打星。

List<Movie> movies = Arrays.asList(
        new Movie("《指环王》", 8.8, true),
        new Movie("《肖申克的救赎》", 9.1, false),
        new Movie("《星球大战》)", 8.0, true),
        new Movie("《我和我的祖国》", 10.0, false));
movies.sort(new Comparator<Movie>() 
    @Override
    public int compare(Movie m1, Movie m2) 
        if(m1.getStarred() == m2.getStarred())
            return 0;
        
        return m1.getStarred() ? -1 : 1;
     
);
movies.forEach(System.out::println);

打印是:

Java8Pro.Movie(title=《指环王》, rating=8.8, starred=true)
Java8Pro.Movie(title=《星球大战》), rating=8.0, starred=true)
Java8Pro.Movie(title=《肖申克的救赎》, rating=9.1, starred=false)
Java8Pro.Movie(title=《我和我的祖国》, rating=10.0, starred=false)

当然,我们也可以使用 lambda 表达式代替匿名类,如下所示:

movies2.sort((m1, m2) -> 
          if(m1.getStarred() == m2.getStarred())
              return 0;
          
          return m1.getStarred() ? -1 : 1;
      );
      movies2.forEach(System.out::println);

我们还可以再次使用Comparator.comparing():

movies2.sort(Comparator.comparing(Movie::getStarred, (star1, star2) -> 
    if(star1 == star2)
         return 0;
    
    return star1 ? -1 : 1;
));

在最后一个示例中,Comparator.comparing() 使用函数来提取用于排序的键作为第一个参数,并使用 Comparator 作为第二个参数。 该比较器使用 apply 进行比较。 star1 和star2 是布尔值,分别代表m1.getStarred() 和 m2.getStarred() 。

6.使用比较器链排序

在最后一个示例中,我们希望在加星标或不加星标的电影中按评分排序。

List<Movie> movies3 = Arrays.asList(
              new Movie("《指环王》", 8.8, true),
              new Movie("《肖申克的救赎》", 9.1, false),
              new Movie("《星球大战》)", 8.0, true),
              new Movie("《我和我的祖国》", 10.0, false));
      movies3.sort(Comparator.comparing(Movie::getStarred)
              .reversed()
              .thenComparing(Comparator.comparing(Movie::getRating)
                      .reversed())
      );
      movies3.forEach(System.out::println);

打印是:

Java8Pro.Movie(title=《指环王》, rating=8.8, starred=true)
Java8Pro.Movie(title=《星球大战》), rating=8.0, starred=true)
Java8Pro.Movie(title=《我和我的祖国》, rating=10.0, starred=false)
Java8Pro.Movie(title=《肖申克的救赎》, rating=9.1, starred=false)

如你所见,我们首先按星标排序,然后按评分排序,没有星标的也按评分排序。

小结

本文简答介绍了Java 8 中比较器的用法,和Java 7 不同的是,我们有更多的排序选择可共使用。

附:完整代码

public class Java8Pro 
public static void main(String[] args) 
      List<String> cities = Arrays.asList(
              "mickjoust",
              "chengdu",
              "sichuan",
              "China",
              "Java8"
      );
      System.out.println(cities);

      cities.sort(String.CASE_INSENSITIVE_ORDER);
      System.out.println(cities);

      cities.sort(Comparator.naturalOrder());
      System.out.println(cities);

      List<Integer> numbers = Arrays.asList(6, 2, 1, 4, 9);
      System.out.println(numbers);

      numbers.sort(Comparator.naturalOrder());
      System.out.println(numbers);

      List<Movie> movies = Arrays.asList(
              new Movie("《指环王》"),
              new Movie("《肖申克的救赎》"),
              new Movie("《星球大战》"),
              new Movie("《我和我的祖国》"));
      movies.sort(Comparator.comparing(Movie::getTitle));
      movies.forEach(System.out::println);

      List<Movie> movies1 = Arrays.asList(
              new Movie("《指环王》", 8.8),
              new Movie("《肖申克的救赎》", 9.1),
              new Movie("《星球大战》)", 8.0),
              new Movie("《我和我的祖国》", 10.0));
      movies1.sort(Comparator.comparingDouble(Movie::getRating).reversed());
      movies1.forEach(System.out::println);

      List<Movie> movies2 = Arrays.asList(
              new Movie("《指环王》", 8.8, true),
              new Movie("《肖申克的救赎》", 9.1, false),
              new Movie("《星球大战》)", 8.0, true),
              new Movie("《我和我的祖国》", 10.0, false));
//      movies2.sort((m1, m2) -> 
//          if(m1.getStarred() == m2.getStarred())
//              return 0;
//          
//          return m1.getStarred() ? -1 : 1;
//      );
      movies2.sort(Comparator.comparing(Movie::getStarred, (star1, star2) -> 
          if(star1 == star2)
              return 0;
          
          return star1 ? -1 : 1;
      ));
      movies2.forEach(System.out::println);

      List<Movie> movies3 = Arrays.asList(
              new Movie("《指环王》", 8.8, true),
              new Movie("《肖申克的救赎》", 9.1, false),
              new Movie("《星球大战》)", 8.0, true),
              new Movie("《我和我的祖国》", 10.0, false));
      movies3.sort(Comparator.comparing(Movie::getStarred)
              .reversed()
              .thenComparing(Comparator.comparing(Movie::getRating)
                      .reversed())
      );
      movies3.forEach(System.out::println);

  

  @Getter
  @Setter
  @AllArgsConstructor
  @ToString
  static class Movie
      private String title;
      private Double rating;
      private Boolean starred;

      public Movie(String title) 
          this.title = title;
      

      public Movie(String title, Double rating) 
          this.title = title;
          this.rating = rating;
      
  

以上是关于Java 8 进阶手册(XX):使用 Comparator 对列表进行排序的主要内容,如果未能解决你的问题,请参考以下文章

Java 8 进阶手册(XX):使用 Comparator 对列表进行排序

Java 8 进阶手册(XX):使用 Comparator 对列表进行排序

Android 工程师进阶手册(8 年 Android 开发者的成长感悟)

哪位大佬有 《 图灵程序设计丛书:Java进阶高手(套装共8册)》电子版书籍百度网盘资源下载

阿里内部最新最全Java面试进阶手册,能横扫98%的面试官

java First_Number_Second_Number_Comparision