在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) {
//
// order by age asc
Optional.of(Person.noNullPersonList().stream()
.sorted(Comparator.comparingInt(Person::getAge)).collect(Collectors.toList()))
.ifPresent(System.out::println);
// +
// 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) {
// +
// order by age desc
Optional.of(Person.noNullPersonList().stream()
.sorted(Comparator.comparingInt(Person::getAge).reversed()).collect(Collectors.toList()))
.ifPresent(System.out::println);
// +
// 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"
at $comparingInt$7b0bb60$1(:490)
at (:296)
at (:221)
at (:1512)
at $SizedRefSortingSink.end(:348)
at (:483)
at (:472)
at $ReduceOp.evaluateSequential(:708)
at (:234)
at (:499)
at .(: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) {
//
Optional.of(Person.nullablePersonList().stream()
.sorted(Comparator.comparing(Person::getAge, Comparator.nullsFirst(Comparator.naturalOrder()))).collect(Collectors.toList()))
.ifPresent(System.out::println);
//
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()和()
在前面的例子中,我们看到reversed()和()都能进行类似倒序的排序,那么这两个方法有什么区别呢?
两个方法的声明如下:
Comparator.comparing(对象的类名::属性的方法名).reversed();
Comparator.comparing(对象的类名::属性的方法名,Comparator.reverseOrder());
说明:
- reversed()是得到排序结果后再反转,
- ()是对属性按照降序进行排序,
- reversed()在多字段排序时,很容易混乱,不建议使用。
- ()更好理解,也更好用些。
可以通过下面的例子看到两者的区别:
package com.morris.java8.sort;
import java.util.Comparator;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* reversed()与()的区别
*/
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}]