在Java8中,你可以使用Stream接口的sorted()方法来对集合中的元素进行排序。这个方法接受一个Comparator对象作为参数,用于定义排序规则。如果你需要根据多个字段进行排序,你可以链式地调用thenComparing()或thenComparingInt()、thenComparingLong()、thenComparingDouble()等方法。

准备数据:

package com.morris.java8.sort;

import java.util.ArrayList;
import java.util.List;

public class Person {

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

    private String name;

    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public static List<Person> noNullPersonList() {
        List<Person> personList = new ArrayList<>();
        personList.add(new Person("Morris", 18));
        personList.add(new Person("Bob", 16));
        personList.add(new Person("Tom", 20));
        personList.add(new Person("Jack", 18));
        personList.add(new Person("Marry", 21));
        personList.add(new Person("Jim", 17));
        return personList;
    }

    public static List<Person> nullablePersonList() {
        List<Person> personList = noNullPersonList();
        personList.add(new Person("Herry", null));
        personList.add(new Person(null, 19));
        return personList;
    }
}

正序

naturalOrder()表示自然排序(一般是升序),数字的自然顺序是数字顺序,字符串按字母顺序排序,日期按时间顺序排序。

package com.morris.java8.sort;

import java.util.Comparator;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 按年龄正序排列
 */
public class AgeAscOrderDemo {
    public static void main(String[] args) {
        // Comparator.comparingInt
        // order by age asc
        Optional.of(Person.noNullPersonList().stream()
                .sorted(Comparator.comparingInt(Person::getAge)).collect(Collectors.toList()))
                .ifPresent(System.out::println);

        // Comparator.comparing + Comparator.naturalOrder
        // order by age asc
        Optional.of(Person.noNullPersonList().stream()
                .sorted(Comparator.comparing(Person::getAge, Comparator.naturalOrder())).collect(Collectors.toList()))
                .ifPresent(System.out::println);
    }
}

运行结果如下:

[Person{name='Bob', age=16}, Person{name='Jim', age=17}, Person{name='Morris', age=18}, Person{name='Jack', age=18}, Person{name='Tom', age=20}, Person{name='Marry', age=21}]
[Person{name='Bob', age=16}, Person{name='Jim', age=17}, Person{name='Morris', age=18}, Person{name='Jack', age=18}, Person{name='Tom', age=20}, Person{name='Marry', age=21}]

倒序

reverseOrder()表示降序。

package com.morris.java8.sort;

import java.util.Comparator;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 按年龄倒序排列
 */
public class AgeDescOrderDemo {
    public static void main(String[] args) {
        // Comparator.comparingInt + Comparator.reversed
        // order by age desc
        Optional.of(Person.noNullPersonList().stream()
                .sorted(Comparator.comparingInt(Person::getAge).reversed()).collect(Collectors.toList()))
                .ifPresent(System.out::println);

        // Comparator.comparing + Comparator.reverseOrder
        // order by age desc
        Optional.of(Person.noNullPersonList().stream()
                .sorted(Comparator.comparing(Person::getAge, Comparator.reverseOrder())).collect(Collectors.toList()))
                .ifPresent(System.out::println);
    }
}

运行结果如下:

[Person{name='Marry', age=21}, Person{name='Tom', age=20}, Person{name='Morris', age=18}, Person{name='Jack', age=18}, Person{name='Jim', age=17}, Person{name='Bob', age=16}]
[Person{name='Marry', age=21}, Person{name='Tom', age=20}, Person{name='Morris', age=18}, Person{name='Jack', age=18}, Person{name='Jim', age=17}, Person{name='Bob', age=16}]

对null字段排序的处理

没有处理属性的null值,排序时可能会空指针:

package com.morris.java8.sort;

import java.util.Comparator;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 对字段中包含null的数据排序抛出异常
 */
public class NullFieldOrderExceptionDemo {
    public static void main(String[] args) {
        Optional.of(Person.nullablePersonList().stream()
                .sorted(Comparator.comparingInt(Person::getAge)).collect(Collectors.toList()))
                .ifPresent(System.out::println);
    }
}

上面的代码会抛出如下异常:

Exception in thread "main" java.lang.NullPointerException
	at java.util.Comparator.lambda$comparingInt$7b0bb60$1(Comparator.java:490)
	at java.util.TimSort.binarySort(TimSort.java:296)
	at java.util.TimSort.sort(TimSort.java:221)
	at java.util.Arrays.sort(Arrays.java:1512)
	at java.util.stream.SortedOps$SizedRefSortingSink.end(SortedOps.java:348)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:483)
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
	at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
	at com.morris.java8.sort.NullFieldOrderExceptionDemo.main(NullFieldOrderExceptionDemo.java:14)

如果排序的字段中包含null,就要对null进行特殊处理:

  • nullsLast()表示如果属性为null,就放到最后面。
  • nullsFirst()表示如果属性为null,就放到最前面。
package com.morris.java8.sort;

import java.util.Comparator;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 对字段中包含null的数据进行排序
 */
public class NullFieldOrderDemo {
    public static void main(String[] args) {
        // Comparator.nullsFirst
        Optional.of(Person.nullablePersonList().stream()
                .sorted(Comparator.comparing(Person::getAge, Comparator.nullsFirst(Comparator.naturalOrder()))).collect(Collectors.toList()))
                .ifPresent(System.out::println);

        // Comparator.nullsLast
        Optional.of(Person.nullablePersonList().stream()
                .sorted(Comparator.comparing(Person::getAge, Comparator.nullsLast(Comparator.naturalOrder()))).collect(Collectors.toList()))
                .ifPresent(System.out::println);
    }
}

运行结果如下:

[Person{name='Herry', age=null}, Person{name='Bob', age=16}, Person{name='Jim', age=17}, Person{name='Morris', age=18}, Person{name='Jack', age=18}, Person{name='null', age=19}, Person{name='Tom', age=20}, Person{name='Marry', age=21}]
[Person{name='Bob', age=16}, Person{name='Jim', age=17}, Person{name='Morris', age=18}, Person{name='Jack', age=18}, Person{name='null', age=19}, Person{name='Tom', age=20}, Person{name='Marry', age=21}, Person{name='Herry', age=null}]

多字段排序

有时我们还需要对多个字段进行排序。

多个字段排序,先对第一个排序字段排序,当第一个排序字段相同时,会使用第二个排序字段进行排序。

package com.morris.java8.sort;

import java.util.Comparator;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 对多个字段进行排序
 */
public class MultiFieldOrderDemo {
    public static void main(String[] args) {
        // 对年龄升序,对名字升序
        // order by age asc,name asc
        Optional.of(Person.noNullPersonList().stream()
                .sorted(Comparator.comparingInt(Person::getAge).thenComparing(Person::getName)).collect(Collectors.toList()))
                .ifPresent(System.out::println);

        // 对年龄升序,对名字降序
        // order by age asc,name desc
        Optional.of(Person.noNullPersonList().stream()
                .sorted(Comparator.comparingInt(Person::getAge).thenComparing(Person::getName, Comparator.reverseOrder())).collect(Collectors.toList()))
                .ifPresent(System.out::println);

        // 对年龄降序,对名字升序
        // order by age desc,name asc
        Optional.of(Person.noNullPersonList().stream()
                .sorted(Comparator.comparing(Person::getAge, Comparator.reverseOrder()).thenComparing(Person::getName)).collect(Collectors.toList()))
                .ifPresent(System.out::println);

        // 对年龄降序,对名字降序
        // order by age desc,name desc
        Optional.of(Person.noNullPersonList().stream()
                .sorted(Comparator.comparing(Person::getAge, Comparator.reverseOrder()).thenComparing(Person::getName, Comparator.reverseOrder())).collect(Collectors.toList()))
                .ifPresent(System.out::println);

    }
}

运行结果如下:

[Person{name='Bob', age=16}, Person{name='Jim', age=17}, Person{name='Jack', age=18}, Person{name='Morris', age=18}, Person{name='Tom', age=20}, Person{name='Marry', age=21}]
[Person{name='Bob', age=16}, Person{name='Jim', age=17}, Person{name='Morris', age=18}, Person{name='Jack', age=18}, Person{name='Tom', age=20}, Person{name='Marry', age=21}]
[Person{name='Marry', age=21}, Person{name='Tom', age=20}, Person{name='Jack', age=18}, Person{name='Morris', age=18}, Person{name='Jim', age=17}, Person{name='Bob', age=16}]
[Person{name='Marry', age=21}, Person{name='Tom', age=20}, Person{name='Morris', age=18}, Person{name='Jack', age=18}, Person{name='Jim', age=17}, Person{name='Bob', age=16}]

reversed()和Comparator.reverseOrder()

在前面的例子中,我们看到reversed()和Comparator.reverseOrder()都能进行类似倒序的排序,那么这两个方法有什么区别呢?

两个方法的声明如下:

Comparator.comparing(对象的类名::属性的方法名).reversed();
Comparator.comparing(对象的类名::属性的方法名,Comparator.reverseOrder());

说明:

  • reversed()是得到排序结果后再反转,
  • Comparator.reverseOrder()是对属性按照降序进行排序,
  • reversed()在多字段排序时,很容易混乱,不建议使用。
  • Comparator.reverseOrder()更好理解,也更好用些。

可以通过下面的例子看到两者的区别:

package com.morris.java8.sort;

import java.util.Comparator;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * reversed()与Comparator.reverseOrder()的区别
 */
public class ReverseOrderDemo {
    public static void main(String[] args) {
        // 对年龄升序,对名字降序
        // order by age asc,name desc
        Optional.of(Person.noNullPersonList().stream()
                .sorted(Comparator.comparingInt(Person::getAge).thenComparing(Person::getName, Comparator.reverseOrder())).collect(Collectors.toList()))
                .ifPresent(System.out::println);

        // 对年龄升序,对名字升序
        // order by age asc,name asc
        Optional.of(Person.noNullPersonList().stream()
                .sorted(Comparator.comparingInt(Person::getAge).thenComparing(Person::getName)).collect(Collectors.toList()))
                .ifPresent(System.out::println);

        // 对上面例子的结果进行反转
        // 先order by age asc,name asc,然后进行反转,实际上就是order by age desc,name desc
        Optional.of(Person.noNullPersonList().stream()
                .sorted(Comparator.comparingInt(Person::getAge).thenComparing(Person::getName).reversed()).collect(Collectors.toList()))
                .ifPresent(System.out::println);

        // order by age desc,name desc
        Optional.of(Person.noNullPersonList().stream()
                .sorted(Comparator.comparing(Person::getAge, Comparator.reverseOrder()).thenComparing(Person::getName, Comparator.reverseOrder())).collect(Collectors.toList()))
                .ifPresent(System.out::println);

    }
}

运行结果如下:

[Person{name='Bob', age=16}, Person{name='Jim', age=17}, Person{name='Morris', age=18}, Person{name='Jack', age=18}, Person{name='Tom', age=20}, Person{name='Marry', age=21}]
[Person{name='Bob', age=16}, Person{name='Jim', age=17}, Person{name='Jack', age=18}, Person{name='Morris', age=18}, Person{name='Tom', age=20}, Person{name='Marry', age=21}]
[Person{name='Marry', age=21}, Person{name='Tom', age=20}, Person{name='Morris', age=18}, Person{name='Jack', age=18}, Person{name='Jim', age=17}, Person{name='Bob', age=16}]
[Person{name='Marry', age=21}, Person{name='Tom', age=20}, Person{name='Morris', age=18}, Person{name='Jack', age=18}, Person{name='Jim', age=17}, Person{name='Bob', age=16}]