在某些特殊的场景下,我们需要在 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 进行排序处理(否则会报错)。

卒然临之而不惊,无故加之而不怒。享受平凡生活中的喜悦,终身成长者。

博主:80 后程序员。爱好:读书、写作和慢跑。

公众号:Java面试真题解析

Java中List排序的3种方法的更多相关文章

  1. Java中创建数组的几种方法

    Java中创建数组的几种方法 public static void main(String[] args) { //创建数组的第一种方法 int[] arr=new int[6]; int intVa ...

  2. 谈谈java中遍历Map的几种方法

    java中的map遍历有多种方法,从最早的Iterator,到java5支持的foreach,再到java8 Lambda,让我们一起来看下具体的用法以及各自的优缺点 先初始化一个map public ...

  3. Java中遍历map的四种方法 - 转载

    在Java中如何遍历Map对象 How to Iterate Over a Map in Java 在java中遍历Map有不少的方法.我们看一下最常用的方法及其优缺点. 既然java中的所有map都 ...

  4. Java 中 List 分片的 5 种方法!

    前些天在实现 MyBatis 批量插入时遇到了一个问题,当批量插入的数据量比较大时,会导致程序执行报错,如下图所示: 原因是 MySQL 只能执行一定长度的 SQL 语句,但当插入的数据量较多时,会生 ...

  5. JAVA中创建线程的三种方法及比较

    JAVA中创建线程的方式有三种,各有优缺点,具体如下: 一.继承Thread类来创建线程 1.创建一个任务类,继承Thread线程类,因为Thread类已经实现了Runnable接口,然后重写run( ...

  6. java中遍历map的几种方法介绍

          喜欢用Java写程序的朋友都知道,我们常用的一种数据结构map中存储的是键值对,我们一般存储的方式是: map.put(key, value); 而提取相应键的值用的方法是: map.ge ...

  7. 干货 | Java中获取类名的3种方法!

    获取类名的方法 Java 中获取类名的方式主要有以下三种. getName() 返回的是虚拟机里面的class的类名表现形式. getCanonicalName() 返回的是更容易理解的类名表示. g ...

  8. Java中终止线程的三种方法

    终止线程一般建议采用的方法是让线程自行结束,进入Dead(死亡)状态,就是执行完run()方法.即如果想要停止一个线程的执行,就要提供某种方式让线程能够自动结束run()方法的执行.比如设置一个标志来 ...

  9. java中创建线程的几种方法及区别

    1,实现Runnable接口创建线程 特点: A:将代码和数据分开,形成清晰的模型 B:线程体run()方法所在的类可以从其它类中继承一些有用的属性和方法 C:有利于保持程序风格的一致性 2,继承Th ...

随机推荐

  1. C/C++ Qt ListWidget 列表框组件应用

    ListWidget列表框组件,该组件与TreeWidget有些相似,区别在于TreeWidget可以实现嵌套以及多字段结构,而ListWidget组件则只能实现单字段结构,ListWidget组件常 ...

  2. 『学了就忘』Linux权限管理 — 55、文件特殊权限

    目录 1.文件特殊权限说明 2.设置SetUID 3.检测SetUID的脚本 4.设置SetGID (1)针对文件的作用 (2)针对目录的作用 5.Sticky BIT 6.设定文件特殊权限 7.文件 ...

  3. 【Python】批量给图片增加水印工具

    背景 最近有一些图片需要增加水印,找了一圈也没看见比较好的工具,又不想用破解的PS,干脆自己做了一个GUI工具,有需要的同学自取 功能 支持水印预览 自定义水印文字内容 支持行楷和微软雅黑两种字体 支 ...

  4. SA 复习笔记

    大家好,由于蒟蒻 tzc 最近被动态点分治这个学也学不会的毒瘤玩意儿虐得不轻,所以就准备换换脑筋来 Van 同样学也学不会的后缀数组了. 考虑一个非常经典的问题:[模板]后缀排序. 一些定义(very ...

  5. [NOI2021] 量子通信

    嗯. NOI2021最白给的一题. PS:很后悔没打同步赛,少了一张同步赛Ag 考虑加黑的256位01串,我们思考一下. 因为\(k\)小于16,所以我们直接分成16块.所以一定可以的绝对有一块是完全 ...

  6. [R] 保存pheatmap图片对象到文件

    一般我们使用pheatmap通过Rstudio交互得到的图片在plots的Export导出即可,如何保存对象到文件呢?这个需求在自动化流程中很常见,作者似乎也没说明. 生成示例数据: test = m ...

  7. 安卓手机添加系统证书方法(HTTPS抓包)

    目录 1. 导出证书(以Charles为例) 2. 安卓证书储存格式 3. 将导出的证书计算hash值 4. 生成系统系统预设格式证书文件 5. 上传证书 安卓7.0以后,安卓不信任用户安装的证书,所 ...

  8. linux—查看所有的账号以及管理账号

    用过Linux系统的人都知道,Linux系统查看用户不是会Windows那样,鼠标右键看我的电脑属性,然后看计算机用户和组即可. 那么Linux操作系统里查看所有用户该怎么办呢?用命令.其实用命令就能 ...

  9. Web网页服务器软件——介绍

    Web网页服务器软件与硬件服务器的关系,就像软件和电脑的关系. 目前有,世界使用排列第一名的Apache.还有可以在Linux系统下快速方便地搭建出LNMP Web服务环境的Nginx(其中LNMP分 ...

  10. (数据科学学习手札132)Python+Fabric实现远程服务器连接

    本文示例代码及文件已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 日常工作中经常需要通过SSH连接到多台远程 ...