Java中List排序的3种方法

Posted 老程不秃

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java中List排序的3种方法相关的知识,希望对你有一定的参考价值。

在某些特殊的场景下,我们需要在 Java 程序中对 List 集合进行排序操作。比如从第三方接口中获取所有用户的列表,但列表默认是以用户编号从小到大进行排序的,而我们的系统需要按照用户的年龄从大到小进行排序,这个时候,我们就需要对 List 集合进行自定义排序操作了。​

List 排序的常见方法有以下 3 种:

  1. 使用 Comparable 进行排序;
  2. 使用 Comparator 进行排序;
  3. 如果是 JDK 8 以上的环境,也可以使用 Stream 流进行排序。

下面我们分别来看各种排序方法的具体实现。

1.使用 Comparable 排序

按照本文设计的场景,我们需要创建一个包含了用户列表的 List 集合,并按用户的年龄从大到小进行排序,具体实现代码如下:

public class ListSortExample 
    public static void main(String[] args) 
        // 创建并初始化 List
        List<Person> list = new ArrayList<Person>() 
            add(new Person(1, 30, "北京"));
            add(new Person(2, 20, "西安"));
            add(new Person(3, 40, "上海"));
        ;
        // 使用 Comparable 自定的规则进行排序
        Collections.sort(list);
        // 打印 list 集合
        list.forEach(p -> 
            System.out.println(p);
        );
    


//  以下 set/get/toString 使用的是 lombok 的注解
@Getter
@Setter
@ToString
class Person implements Comparable<Person> 
    private int id;
    private int age;
    private String name;

    public Person(int id, int age, String name) 
        this.id = id;
        this.age = age;
        this.name = name;
    

    @Override
    public int compareTo(Person p) 
        return p.getAge() - this.getAge();
    

复制代码

以上代码的执行结果,如下图所示: 

 本方法的核心代码如下: 

2.使用 Comparator 排序

Comparable 是类内部的比较方法,而 Comparator 是排序类外部的比较器。使用 Comparator 比较器,无需修改原 Person 类,只需要扩充一个 Person 类的比较器就行了,Comparator 的实现方法有以下两种:

  • 新建 Comparator 比较器;
  • 使用 Comparator 匿名类比较器。

其中,第二种实现方法要更简洁一些,我们通过下面的具体代码,来观察一下二者的区别。

2.1 新建 Comparator 比较器

public class ListSortExample2 
    public static void main(String[] args) 
        // 创建并初始化 List
        List<Person> list = new ArrayList<Person>() 
            add(new Person(1, 30, "北京"));
            add(new Person(2, 20, "西安"));
            add(new Person(3, 40, "上海"));
        ;
        // 使用 Comparator 比较器排序
        Collections.sort(list, new PersonComparator());
        // 打印 list 集合
        list.forEach(p -> 
            System.out.println(p);
        );
    

/**
 * 新建 Person 比较器
 */
class PersonComparator implements Comparator<Person> 
    @Override
    public int compare(Person p1, Person p2) 
        return p2.getAge() - p1.getAge();
    

@Getter
@Setter
@ToString
class Person 
    private int id;
    private int age;
    private String name;

    public Person(int id, int age, String name) 
        this.id = id;
        this.age = age;
        this.name = name;
    

复制代码

以上代码的执行结果,如下图所示: 

 本方法的核心实现代码如下: 

2.2 匿名类比较器

比较器 Comparator 可以使用更简洁的匿名类的方式,来实现排序功能,具体实现代码如下:

public class ListSortExample2 
    public static void main(String[] args) 
        // 创建并初始化 List
        List<Person> list = new ArrayList<Person>() 
            add(new Person(1, 30, "北京"));
            add(new Person(2, 20, "西安"));
            add(new Person(3, 40, "上海"));
        ;
        // 使用匿名比较器排序
        Collections.sort(list, new Comparator<Person>() 
            @Override
            public int compare(Person p1, Person p2) 
                return p2.getAge() - p1.getAge();
            
        );
        // 打印 list 集合
        list.forEach(p -> 
            System.out.println(p);
        );
    

@Getter
@Setter
@ToString
class Person 
    private int id;
    private int age;
    private String name;
    public Person(int id, int age, String name) 
        this.id = id;
        this.age = age;
        this.name = name;
    

复制代码

以上代码的执行结果,如下图所示: 

3.使用 Stream 流排序

在 JDK 8 之后可以使用更加简单的方法 Stream 流来实现排序功能,它的实现只需要一行代码,具体实现如下:

public class ListSortExample3 
    public static void main(String[] args) 
        // 创建并初始化 List
        List<Person> list = new ArrayList<Person>() 
            add(new Person(1, 30, "北京"));
            add(new Person(2, 20, "西安"));
            add(new Person(3, 40, "上海"));
        ;
        // 使用 Stream 排序
        list = list.stream().sorted(Comparator.comparing(Person::getAge).reversed())
                .collect(Collectors.toList());
        // 打印 list 集合
        list.forEach(p -> 
            System.out.println(p);
        );
    
    @Getter
    @Setter
    @ToString
    static class Person 
        private int id;
        private int age;
        private String name;
        public Person(int id, int age, String name) 
            this.id = id;
            this.age = age;
            this.name = name;
        
    

复制代码

其中 reversed() 表示倒序的意思,如果不使用此方法则是正序。

以上代码的执行结果,如下图所示: 

扩展:排序字段为 null

使用 Stream 进行排序时,如果排序的字段出现 null 值就会导致异常发生,具体示例如下:

public class ListSortExample4 
    public static void main(String[] args) 
        // 创建并初始化 List
        List<Person> list = new ArrayList<Person>() 
            add(new Person(30, "北京"));
            add(new Person(10, "西安"));
            add(new Person(40, "上海"));
            add(new Person(null, "上海")); // 年龄为 null 值
        ;
        // 按照[年龄]正序,但年龄中有一个 null 值
        list = list.stream().sorted(Comparator.comparing(Person::getAge))
                .collect(Collectors.toList());
        // 打印 list 集合
        list.forEach(p -> 
            System.out.println(p);
        );
    

@Getter
@Setter
@ToString
class Person 
    private Integer age;
    private String name;

    public Person(Integer age, String name) 
        this.age = age;
        this.name = name;
    

复制代码

以上代码的执行结果,如下图所示: 

 想要解决上述问题,需要给 Comparator.comparing 传递第二个参数:Comparator.nullsXXX,如下代码所示:

public class ListSortExample4 
    public static void main(String[] args) 
        // 创建并初始化 List
        List<Person> list = new ArrayList<Person>() 
            add(new Person(30, "北京"));
            add(new Person(10, "西安"));
            add(new Person(40, "上海"));
            add(new Person(null, "上海"));
        ;
        // 按照[年龄]正序,但年龄中有一个 null 值
        list = list.stream().sorted(Comparator.comparing(Person::getAge,
                Comparator.nullsFirst(Integer::compareTo)))
                .collect(Collectors.toList());
        // 打印 list 集合
        list.forEach(p -> 
            System.out.println(p);
        );
    

@Getter
@Setter
@ToString
class Person 
    private Integer age;
    private String name;

    public Person(Integer age, String name) 
        this.age = age;
        this.name = name;
    

复制代码

Comparator.nullsFirst 表示将排序字段中的 null 值放到集合最前面,如果想要将 null 值放到集合最后面可以使用 Comparator.nullsLast。

以上代码的执行结果,如下图所示: 

总结

本文介绍了 3 种 List 排序的方法,前两种方法常用于 JDK 8 之前的版本,其中比较器 Comparator 有两种实现的写法,而在 JDK 8 之后的版本,就可以使用 Comparator.comparing 实现排序了,如果排序字段中可能出现 null 值,要使用 Comparator.nullsXXX 进行排序处理(否则会报错)。​

通过Java排序List集合的元素的几种方法

用Java工具类Collectionssort()方法,对List集合元素进行排序。

Collections提供两种排序方法:

一、Collections.sort(List<T> list);

  此方法需要泛型T这个Bean实现Comparable<T>接口,并且实现compareTo()方法排序;

二、Collections.sort(List<T> list, Comparator<? super T> c);

  此方法,在泛型T这个Bean没有实现Comparable<T>接口的时候,多个一个参数,是一个接口我们需要实现其compare()方法排序;

排序List集合里面的元素,例如:

 1 /**
 2 * 简单的Collection排序
 3 */
 4 public static void simpleSort() {
 5     List<String> sortElement = new ArrayList<>();
 6     sortElement.add("A");
 7     sortElement.add("D");
 8     sortElement.add("R");
 9     sortElement.add("T");
10     sortElement.add("F");
11     System.out.println("未排序之前的List:" + sortElement);
12     Collections.sort(sortElement);
13     System.out.println("排序之后的List:" + sortElement);
14 }

输出结果:

未排序之前的List:[A, D, R, T, F]
排序之后的List:[A, D, F, R, T]

由于String类其实自身已经实现了Comparable接口,Java已经帮我们封装好了,所以我们不需要再实现compareTo()方法;

下面来看下,新建一个Bean,实现Comparable<T>接口,并且实现compareTo()方法来自定义排序。例如:

新建Bean:JavaProgrammer :

 1 package com.max.basis;
 2 
 3 import java.io.Serializable;
 4 
 5 /**
 6  * Java程序员
 7  * @author Max.
 8  * @date 2018/7/20
 9  */
10 public class JavaProgrammer implements Comparable<JavaProgrammer>, Serializable {
11     /**
12      * 姓名
13      */
14     private String name;
15     /**
16      * 工资
17      */
18     private int wage;
19     /**
20      * 年龄
21      */
22     private int age;
23 
24     @Override
25     public int compareTo(JavaProgrammer o) {
26         // 首先根据年龄排序
27         int sort = this.getAge() - o.getAge();
28         // 返回值0代表相等,1表示大于,-1表示小于;
29         if (sort == 0) {
30             // 在根据工资排序
31             return this.getWage() - o.getWage();
32         }
33         return sort;
34     }
35 
36     public JavaProgrammer(String name, int wage, int age) {
37         this.name = name;
38         this.wage = wage;
39         this.age = age;
40     }
41 
42     public String getName() {
43         return name;
44     }
45 
46     public void setName(String name) {
47         this.name = name;
48     }
49 
50     public int getWage() {
51         return wage;
52     }
53 
54     public void setWage(int wage) {
55         this.wage = wage;
56     }
57 
58     public int getAge() {
59         return age;
60     }
61 
62     public void setAge(int age) {
63         this.age = age;
64     }
65 }
 1 /**
 2 * Bean实现Comparable接口,实现compareTo()方法来排序
 3 */
 4 public static void sortBeans() {
 5     List<JavaProgrammer> sortBeans = new ArrayList<>();
 6     sortBeans.add(new JavaProgrammer("A", 20000, 20));
 7     sortBeans.add(new JavaProgrammer("B", 55000, 21));
 8     sortBeans.add(new JavaProgrammer("C", 65000, 20));
 9     sortBeans.add(new JavaProgrammer("D", 120000, 28));
10     sortBeans.add(new JavaProgrammer("E", 90000, 23));
11     Collections.sort(sortBeans);
12     for (JavaProgrammer javaProgrammer : sortBeans) {
13         System.out.println("姓名:" + javaProgrammer.getName()
14             + ",工资:" + javaProgrammer.getWage()
15             + ",年龄:" + javaProgrammer.getAge());
16     }
17 }

输出结果:

姓名:A,工资:20000,年龄:20
姓名:C,工资:65000,年龄:20
姓名:B,工资:55000,年龄:21
姓名:E,工资:90000,年龄:23
姓名:D,工资:120000,年龄:28

看到上面的Bean,实现了Comparable<T>接口,并且实现compareTo()方法,首先根据年龄大小排序,如果年龄相等,在根据工资大小排序;

再看不在Bean里面实现Comparable<T>接口,在需要用到排序的时候,用Collections.sort(List<T> list, Comparator<? super T> c)方法排序,例如:

新建Bean:TestBean :

 1 package com.max.basis;
 2 
 3 import java.io.Serializable;
 4 
 5 /**
 6  * 测试类
 7  * @author Max.
 8  * @date 2018/7/20
 9  */
10 public class TestBean implements Serializable {
11 
12     private Integer age;
13 
14     private Integer score;
15 
16     public TestBean(Integer age, Integer score) {
17         this.age = age;
18         this.score = score;
19     }
20 
21     public Integer getAge() {
22         return age;
23     }
24 
25     public void setAge(Integer age) {
26         this.age = age;
27     }
28 
29     public Integer getScore() {
30         return score;
31     }
32 
33     public void setScore(Integer score) {
34         this.score = score;
35     }
36 }
 1 /**
 2 * 在Collection排序的时候给人Comparator参数
 3 */
 4 private static void sortTestBean() {
 5     List<TestBean> sortList = new ArrayList<>();
 6     sortList.add(new TestBean(18, 402));
 7     sortList.add(new TestBean(18, 512));
 8     sortList.add(new TestBean(17, 633));
 9     sortList.add(new TestBean(19, 497));
10     Collections.sort(sortList, new Comparator<TestBean>() {
11         @Override
12         public int compare(TestBean o2, TestBean o1) {
13             int sort = o1.getAge() - o2.getAge();
14             if (sort == 0) {
15                 return o1.getScore() - o2.getScore();
16             }
17             return sort;
18         }
19     });
20     for (TestBean testBean : sortList) {
21         System.out.println("年龄:" + testBean.getAge() 
22             + ",总分:" + testBean.getScore());
23     }
24 }

输出结果:

年龄:19,总分:497
年龄:18,总分:512
年龄:18,总分:402
年龄:17,总分:633

 注意:如果需要排序或者降序,只需要把o1.getAge() - o2.getAge()两个互换位置就可以了。

然而上面的例子只是对List集合的英文及数字排序,再来看看Collections.sort 中文排序。

 

public static void sortString(){
    List<String> sortStringList = new ArrayList<>();
    sortStringList.add("刘邦");
    sortStringList.add("项羽");
    sortStringList.add("关羽");
    sortStringList.add("赵云");
    sortStringList.add("诸葛亮");
    sortStringList.add("曹操");
    sortStringList.add("曹操到");
    sortStringList.add("LOL");
    sortStringList.add("赵高");
    sortStringList.add("10086");
    System.out.println("未排序之前的List:" + sortStringList);
    Comparator<String> comparator = new Comparator<String>() {
        @Override
        public int compare(String o1, String o2) {
            Collator collator = Collator.getInstance();
            return collator.getCollationKey(o1).compareTo(collator.getCollationKey(o2));
        }
    };
    Collections.sort(sortStringList,comparator);
    System.out.println("排序之后的List:" + sortStringList);
}

 

输出结果:

未排序之前的List:[刘邦, 项羽, 关羽, 赵云, 诸葛亮, 曹操, 曹操到, LOL, 赵高, 10086]
排序之后的List:[10086, LOL, 曹操, 曹操到, 关羽, 刘邦, 项羽, 赵高, 赵云, 诸葛亮]

排序规则是:数字排在最前,英文字母其次,汉字则按照拼音进行排序。

 

以上是关于Java中List排序的3种方法的主要内容,如果未能解决你的问题,请参考以下文章

java List的排序

java中list排序

Java List集合排序的两种方法

java List 排序

java list排序的两种方式(实现Comparable接口和Collections.sort重载方法)

Java中List的排序