Java List排序 java ListMap 排序 Java listmap 模拟 oracle 排序 Java listmap 模拟 mysql 排序
一、概述
近期的开发工作中,遇到一个需求: 对 Listmap的结果集进行排序,类似于模拟 数据库中的Order By 排序。那么主要的需求有: 排序方式 ASC/DESC ; null 值处理:NULL FIRST / NULL LAST 。 在Java 中要实现以上相应的功能,对List进行排序,会遇到对应问题: 元素中有 null ,排序时候会抛出 NPE 、 null 元素放在 最前、最后面的问题。
本文将基于Java8,逐一演示,上述中出现问题的对应示例代码;最终给出 List<Map> 排序的解决方案,实现易用性,通用性。
二、问题
1、元素有null,排序处理:
/**
* Description: null 值处理
* <br> 1. null 元素,排序 NPE
* <br> 2. 解决: null 放在最前面、最后面
* @param
* @return void
* @version v1.0
* @author wu
* @date 2022/10/17 15:43
*/
@Test
public void nullListTest(){
ArrayList<String> list = Lists.newArrayList("22","33",null,"11");
System.out.println("nullListTest 排序前:" + list);
// list中有 null 元素,排序会抛出 NPE
list.sort(Comparator.comparing(e->e)); // 抛出 NPE
list.sort((e1,e2)->e1.compareTo(e2)); // 抛出 NPE
// list.sort(Comparator.comparing(String::valueOf)); // 避免npe
// null 值处理: null 放在最前
list.sort(Comparator.comparing(e->e,Comparator.nullsFirst(String::compareTo)));
System.out.println("nullListTest 排序后: null 放在最前:" + list);
// null 值处理: null 放在最后
list.sort(Comparator.comparing(e->e,Comparator.nullsLast(String::compareTo)));
System.out.println("nullListTest 排序后:null 放在最后:" + list);
}
1.1、输出结果:
nullListTest 排序前:[22, 33, null, 11]
java.lang.NullPointerException
...
2、倒序 DESC时,null 值放在 最前面、最后面的处理
/**
* Description: 倒序的时候, null 值处理
* <br> 1. null 最前面: nullsLast 方法
* <br> 2. null 最后面: nullsFirst 方法
* @return void
* @version v1.0
* @author wu
* @date 2022/10/17 16:37
*/
@Test
public void nullDESCTest() throws Exception{
ArrayList<String> list = Lists.newArrayList("22","33",null,"11");
System.out.println("nullDESCTest 排序前:" + list);
// DESC , null first
// list.sort(Comparator.comparing((String e)->e,Comparator.nullsFirst(String::compareTo)).reversed());
list.sort(Comparator.comparing((String e)->e,Comparator.nullsLast(String::compareTo)).reversed());
System.out.println("nullDESCTest DESC , null first :" + list);
// DESC , null last
list.sort(Comparator.comparing((String e)->e,Comparator.nullsFirst(String::compareTo)).reversed());
System.out.println("nullDESCTest DESC , null last :" + list);
}
2.1、输出结果:
nullDESCTest 排序前:[22, 33, null, 11]
nullDESCTest DESC , null first :[null, 33, 22, 11]
nullDESCTest DESC , null last :[33, 22, 11, null]
3、数字是字符串类型时,排序不对的问题:
/**
* Description: 字符串类型数字 排序
* <br> 解决:字符串转换为数字,进行排序
* @return void
* @version v1.0
* @author wu
* @date 2022/10/17 15:44
*/
@Test
public void strNumTest() throws Exception{
List<String> list = Lists.newArrayList("10","11","101","111");
System.out.println("strNumTest 排序前:" + list);
// 倒序排序
list.sort(Comparator.comparing(String::valueOf).reversed());
// list.sort(Comparator.comparing((String e)->Integer.valueOf(e)).reversed()); // 解决排序不对的问题
System.out.println("strNumTest 排序后:" + list);
}
3.1、输出结果是:
strNumTest 排序前:[10, 11, 101, 111]
strNumTest 排序后:[111, 11, 101, 10]
三、解决 --- 通用排序方案
1、上述代码中,演示了可能出现的问题,以及对应的解决方案:
- 元素中有null , 使用 Comparator.nullsFirst / Comparator.nullsLast
- 倒序时null元素 处理: 最前面 --- Comparator.nullsLast ; 最后面 --- Comparator.nullsFirst
- 数字是字符串类型时: 转换为数字进行排序
2、List<Map<String,Object>> 排序可能遇到问题:
- 排序字段不确定
- 排序字段类型不确定
- 排序方式不确定
- 可能 出现null元素
3、经整理汇总后,通用排序流程如下:
4、定义一个 ListMapConfig 配置类 ,统一维护相关的配置:
/**
* Description: List Map 排序 配置
* @author w
* @version 1.0
* @date 2022/10/17 16:09
*/
public class ListMapConfig {
private String orderProperty ; // 排序字段:
private String porpertyType = "STRING"; // 属性类型: STRING / NUMBER
private String orderMode ="ASC"; // 排序方式: ASC/DESC
private String orderModeNull ="LAST"; // null值处理: FIRST / LAST
public static ListMapConfig getDefault(){
return new ListMapConfig();
}
// ignore getter / setter
}
5、通用的 ListMap排序方法:
public static void processSort(List<Map<String, Object>> listMap , ListMapConfig config){
String orderProperty = config.getOrderProperty();
Assert.isTrue(StringUtils.isNotBlank(orderProperty));
if("NUMBER".equals(config.getPorpertyType())){
// 数字 类型排序
if("DESC".equals(config.getOrderMode())){
if("FIRST".equals(config.getOrderModeNull())){
listMap.sort(Comparator.comparing((Map e)->{
Object val = e.get(orderProperty);
return val == null ? null : Double.valueOf(val.toString());
// 倒序时,null值放在最后,反过来:需要用 nullsLast
},Comparator.nullsLast(Double::compareTo)).reversed());
}else {
listMap.sort(Comparator.comparing((Map e)->{
Object val = e.get(orderProperty);
return val == null ? null : Double.valueOf(val.toString());
// 倒序时,null值放在最后,反过来:需要用 nullsFirst
},Comparator.nullsFirst(Double::compareTo)).reversed());
}
}else {
if("FIRST".equals(config.getOrderModeNull())){
listMap.sort(Comparator.comparing((Map e)->{
Object val = e.get(orderProperty);
return val == null ? null : Double.valueOf(val.toString());
},Comparator.nullsFirst(Double::compareTo)));
}else {
listMap.sort(Comparator.comparing((Map e)->{
Object val = e.get(orderProperty);
return val == null ? null : Double.valueOf(val.toString());
},Comparator.nullsFirst(Double::compareTo)));
}
}
}else if("STRING".equals(config.getPorpertyType())){
// 字符串 类型排序
if("DESC".equals(config.getOrderMode())){
if("FIRST".equals(config.getOrderModeNull())){
listMap.sort(Comparator.comparing((Map e) -> {
Object val = e.get(orderProperty);
return null == val ? null : val.toString();
// 倒序时,null值放在最后,反过来:需要用 nullsLast
},Comparator.nullsLast(String::compareTo)).reversed());
}else {
listMap.sort(Comparator.comparing((Map e) -> {
Object val = e.get(orderProperty);
return null == val ? null : val.toString();
// 倒序时,null值放在最后,反过来:需要用 nullsFirst
},Comparator.nullsFirst(String::compareTo)).reversed());
}
}else {
if("FIRST".equals(config.getOrderModeNull())){
listMap.sort(Comparator.comparing((Map e) -> {
Object val = e.get(orderProperty);
return null == val ? null : val.toString();
},Comparator.nullsFirst(String::compareTo)));
}else {
listMap.sort(Comparator.comparing((Map e) -> {
Object val = e.get(orderProperty);
return null == val ? null : val.toString();
},Comparator.nullsLast(String::compareTo)));
}
}
}
}
四、代码测试
1、创建一个 ListMapSortTest2 测试类:
public class ListMapSortTest2 {
static List<Order> orderList = null;
static List<Map<String,Object>> listMap = null;
public static void main(String[] args) {
init();
System.out.println(orderList);
// 1、转换为map
listMap = com.google.common.collect.Lists.newArrayList();
for (Order e : orderList) {
JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(e));
Map map = JSON.toJavaObject(jsonObject, Map.class);
listMap.add(map);
}
System.out.println("listMap :"+listMap);
ListMapConfig config = ListMapConfig.getDefault();
config.setOrderProperty("appNo");
// STRING-TEST1: ASC - NULL FIRST
config.setOrderModeNull("FIRST");
processSort(listMap,config);
System.out.println("STRING-TEST1: ASC - NULL FIRST : " + listMap);
// STRING-TEST2: ASC - NULL LAST
config.setOrderModeNull("LAST");
processSort(listMap,config);
System.out.println("STRING-TEST2: ASC - NULL LAST : " + listMap);
// STRING-TEST3: DESC - NULL FIRST
config.setOrderModeNull("FIRST");
config.setOrderMode("DESC");
processSort(listMap,config);
System.out.println("STRING-TEST3: DESC - NULL FIRST : " + listMap);
// STRING-TEST4: DESC - NULL LAST
config.setOrderModeNull("LAST");
config.setOrderMode("DESC");
processSort(listMap,config);
System.out.println("STRING-TEST4: DESC - NULL LAST : " + listMap);
}
private static void init() {
orderList = Lists.newArrayList();
orderList.add(new Order(1,"小明","203"));
orderList.add(new Order(2,"小红",null));
orderList.add(new Order(3,"小刚","404"));
}
static class Order {
private Integer id;
private String name;
private String appNo;
public Order(Integer id, String name, String appNo) {
this.id = id;
this.name = name;
this.appNo = appNo;
}
public Order() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAppNo() {
return appNo;
}
public void setAppNo(String appNo) {
this.appNo = appNo;
}
@Override
public String toString() {
return "Order{" +
"id=" + id +
", name='" + name + '\'' +
", appNo='" + appNo + '\'' +
'}';
}
}
}
3、注意:Number类型,未测试!
五、总结
1、本文初步解决 List<Map> 集合的排序问题,因 map集合中元素类型的不确定性,所以 当前写了2种排序方式:统一按照字符串排序 或者 数字类型 ; 在 ListMapConfig.porpertyType 中,可以添加 相应的其他类型,进行功能扩展 。 比如: java.util.Date 类型处理。
2、ListMapConfig 类中,porpertyType 等属性,可以定义为 枚举类型, 便于更好的理解,和后续扩展。
3、通用排序方法:processSort , 写了太多的 if else , 可以引入设计模式,更加优雅!
排序相关的文章资料参考:
使用MySQL FIELD函数实现自定义排序 特定字段排序 指定字段排序_HaHa_Sir的博客-优快云博客
Java List集合排序 Java8 List集合排序方法 Java Lambda集合排序_HaHa_Sir的博客-优快云博客_java8 集合排序