📌 拿来即用 : Java 企业级工具类全家桶-50-集合排序工具类
📋 目录
一、工具类功能
给业务代码用的集合排序入口:列表按单个字段升/降序、按几个字段依次比较、中文按拼音排,另给两个可直接复用的空值安全比较器,不必再手写 Comparator 样板。
排序原地生效、没有返回值,类本身零第三方依赖(拼音序用 JDK 自带的 Collator)。典型场景是员工先按部门再按薪资、订单按金额倒序、城市与姓名的字典序列表。
二、所需环境
JDK 17或以上
三、方法概览(按需调用)
| 序号 | 方法 | 入参 | 返回 | 用途 |
|---|---|---|---|---|
| 1 | sortAsc(List, Function<T, C>) | 待排序列表 + key提取函数 | void | 按单个字段升序,空值排最前 |
| 2 | sortDesc(List, Function<T, C>) | 待排序列表 + key提取函数 | void | 按单个字段降序,空值排最后 |
| 3 | sortMultiAsc(List, Function<T, ?>...) | 待排序列表 + 依次比较的key提取函数 | void | 多字段依次升序,空值按该字段最小参与比较 |
| 4 | sortMultiDesc(List, Function<T, ?>...) | 待排序列表 + 依次比较的key提取函数 | void | 多字段依次降序,空值落在最后 |
| 5 | sortChinesePinyinAsc(List, boolean) | 待排序列表 + null是否排最前 | void | 中文按拼音升序,null 位置由参数定 |
| 6 | sortChinesePinyinDesc(List, boolean) | 待排序列表 + null是否排最前 | void | 中文按拼音降序,null 位置由参数定 |
| 7 | nullsFirstNatural(Function<T, C>) | key提取函数 | Comparator | 造空值排最前的比较器,交给 list.sort 或 stream 复用 |
| 8 | nullsLastNatural(Function<T, C>) | key提取函数 | Comparator | 造空值排最后、字段仍按升序的比较器 |
四、调用示例(直观体验)
调用示例直接复制进 IDE 右键 Run 就能看到调用工具类各方法的输出结果。
java
21:00:30.789 [main] INFO -- 「SortUtils 方法1 sortAsc(List<T>, Function<T, C>)」原地升序后薪资 -> [-, null, 5000, 7000, 9000]
21:00:30.792 [main] INFO -- 「SortUtils 方法2 sortDesc(List<T>, Function<T, C>)」原地降序后薪资 -> [9000, 7000, 5000, null, -]
21:00:30.794 [main] INFO -- 「SortUtils 方法3 sortMultiAsc(List<T>, Function<T, ?>...)」部门与薪资 -> [-, hr/null, sale/5000, tech/7000, tech/9000]
21:00:30.794 [main] INFO -- 「SortUtils 方法4 sortMultiDesc(List<T>, Function<T, ?>...)」部门与薪资 -> [tech/9000, tech/7000, sale/5000, hr/null, -]
21:00:30.830 [main] INFO -- 「SortUtils 方法5 sortChinesePinyinAsc(List<String>, boolean)」拼音升序(null 在前)-> [null, 安康, 北京, 上海, 台湾, 香港]
21:00:30.831 [main] INFO -- 「SortUtils 方法6 sortChinesePinyinDesc(List<String>, boolean)」拼音降序(null 在后)-> [香港, 台湾, 上海, 北京, 安康, null]
21:00:30.832 [main] INFO -- 「SortUtils 方法7 nullsFirstNatural(Function<T, C>)」交给 list.sort 后薪资 -> [-, null, 5000, 7000, 9000]
21:00:30.840 [main] INFO -- 「SortUtils 方法8 nullsLastNatural(Function<T, C>)」交给 stream.sorted 后薪资 -> [5000, 7000, 9000, null, -]
21:00:30.841 [main] ERROR -- 「异常信息获取演示」不可变列表 -> UnsupportedOperationException
21:00:30.841 [main] ERROR -- 「异常信息获取演示」列表为null -> 待排序列表不能为null
21:00:30.842 [main] ERROR -- 「异常信息获取演示」提取函数为空 -> 至少需要一个key提取函数
21:00:30.843 [main] ERROR -- 「异常信息获取演示」同一字段返回类型不一致 -> class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap')
进程已结束,退出代码为 0
java
package com.example.order.api;
import com.example.common.utils.SortUtils;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 调用示例:整个文件复制进 IDE,右键 Run 就能看效果。
*
* <p>排序工具只吃内存里的列表,不依赖外部服务,也不需要 Spring 容器;示例造一份「薪资有 null、
* 列表里混一个 null 元素」的数据,把 8 个入口各调一遍,空值的落位与多字段依次比较都能直接看出差别。
* 日志用 Lombok 的 {@code @Slf4j} + SLF4J 占位符,没装 Lombok 就把 {@code log} 换成项目自己的 Logger。
*/
@Slf4j
public class SortUtilsDemo {
/** 示例用的员工对象,实际换成你自己的;id=4 那行薪资为 null */
public record Emp(int id, String dept, Integer salary) {
}
public static void main(String[] args) {
// ========== 一、单个字段直接排 ==========
List<Emp> ascList = emps();
SortUtils.sortAsc(ascList, Emp::salary);
log.info("「SortUtils 方法1 sortAsc(List<T>, Function<T, C>)」原地升序后薪资 -> {}", salaries(ascList));
List<Emp> descList = emps();
SortUtils.sortDesc(descList, Emp::salary);
log.info("「SortUtils 方法2 sortDesc(List<T>, Function<T, C>)」原地降序后薪资 -> {}", salaries(descList));
// ========== 二、多个字段依次比较 ==========
List<Emp> multiAscList = emps();
SortUtils.sortMultiAsc(multiAscList, Emp::dept, Emp::salary);
log.info("「SortUtils 方法3 sortMultiAsc(List<T>, Function<T, ?>...)」部门与薪资 -> {}", deptSalary(multiAscList));
List<Emp> multiDescList = emps();
SortUtils.sortMultiDesc(multiDescList, Emp::dept, Emp::salary);
log.info("「SortUtils 方法4 sortMultiDesc(List<T>, Function<T, ?>...)」部门与薪资 -> {}", deptSalary(multiDescList));
// ========== 三、中文拼音排序 ==========
List<String> cityAsc = cities();
SortUtils.sortChinesePinyinAsc(cityAsc, true);
log.info("「SortUtils 方法5 sortChinesePinyinAsc(List<String>, boolean)」拼音升序(null 在前)-> {}", cityAsc);
List<String> cityDesc = cities();
SortUtils.sortChinesePinyinDesc(cityDesc, false);
log.info("「SortUtils 方法6 sortChinesePinyinDesc(List<String>, boolean)」拼音降序(null 在后)-> {}", cityDesc);
// ========== 四、比较器工厂复用 ==========
List<Emp> sortedByComparator = emps();
sortedByComparator.sort(SortUtils.nullsFirstNatural(Emp::salary));
log.info("「SortUtils 方法7 nullsFirstNatural(Function<T, C>)」交给 list.sort 后薪资 -> {}",
salaries(sortedByComparator));
List<String> streamSalaries = emps().stream()
.sorted(SortUtils.nullsLastNatural(Emp::salary))
.map(emp -> emp == null ? "-" : String.valueOf(emp.salary()))
.toList();
log.info("「SortUtils 方法8 nullsLastNatural(Function<T, C>)」交给 stream.sorted 后薪资 -> {}", streamSalaries);
// ========== 五、catch异常信息 ==========
// 1. 原地排序要求列表可变,List.of 的结果会直接拒绝(它抛的异常不带文案,打异常类型)
try {
SortUtils.sortAsc(List.of(new Emp(1, "tech", 9000)), Emp::salary);
} catch (UnsupportedOperationException e) {
log.error("「异常信息获取演示」不可变列表 -> {}", e.getClass().getSimpleName());
}
// 2. 列表为 null
try {
SortUtils.sortAsc(null, Emp::salary);
} catch (IllegalArgumentException e) {
log.error("「异常信息获取演示」列表为null -> {}", e.getMessage());
}
// 3. 一个提取函数都没传
try {
SortUtils.sortMultiAsc(emps());
} catch (IllegalArgumentException e) {
log.error("「异常信息获取演示」提取函数为空 -> {}", e.getMessage());
}
// 4. 同一字段一会儿给字符串一会儿给数字,比较时由 compareTo 抛类型异常
try {
List<Emp> mixed = emps();
SortUtils.sortMultiAsc(mixed, emp -> emp.id() % 2 == 0 ? emp.dept() : emp.salary());
} catch (ClassCastException e) {
log.error("「异常信息获取演示」同一字段返回类型不一致 -> {}", e.getMessage());
}
}
/** 样例数据:薪资有 null,列表里还混了一个 null 元素,排序时它跟着空值规则落位 */
private static List<Emp> emps() {
return new ArrayList<>(Arrays.asList(
new Emp(1, "tech", 9000),
new Emp(2, "sale", 5000),
null,
new Emp(3, "tech", 7000),
new Emp(4, "hr", null)));
}
private static List<String> cities() {
return new ArrayList<>(Arrays.asList("北京", "上海", null, "安康", "台湾", "香港"));
}
/** 只看薪资,null 元素与 null 薪资都写成 - */
private static List<String> salaries(List<Emp> list) {
List<String> out = new ArrayList<>(list.size());
for (Emp emp : list) {
out.add(emp == null ? "-" : String.valueOf(emp.salary()));
}
return out;
}
/** 「部门/薪资」拼成一行,多字段排序的结果一眼能对上 */
private static List<String> deptSalary(List<Emp> list) {
List<String> out = new ArrayList<>(list.size());
for (Emp emp : list) {
out.add(emp == null ? "-" : emp.dept() + "/" + emp.salary());
}
return out;
}
}
五、工具类源码(可直接复制)
📦 「点击此处可下载 Java企业级工具类全家桶-50-集合排序工具类」 的完整源码,拿来即用。
觉得对您有帮助,麻烦 点点关注啦 ,您的关注是我创作的最大动力~ 🎯