Java stream流支持多字段排序
背景
对于排序而言,比较常见的场景是前端传递所需的排序字段名和排序方向,然后通过stream流或者数据库来实现排序.
为动态接收参数,继承Map来支持多字段传入.另外stream流原生的sorted写起来相对比较繁琐,通过compartor方法封装构建多字段排序的逻辑.具体就是通过反射拿到对应字段的值,然后利用Compartor的comparing和thenComparing完成多字段排序.
代码
具体代码和测试结果如下
package xyz.yq56;import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;import cn.hutool.core.util.ReflectUtil;
import lombok.Data;/*** 封装Map接收前端排序参数,并提供compartor,方便构建stream流sorted所需的Comparator* <pre>* 示例:* {* "fieldName": "ascend",* "fieldName2": "descend"* }* </pre>** <pre>* 1. key 是排序字段* 2. value 是排序方向,可选值 ascend/descend 或者 asc/desc* 3. 排序字段属性需符合java的get规范,其中使用了反射获取field对应的get方法* 4. 排序字段的类型必须实现Comparable接口,否则抛出异常The field xx does not implement Comparable interface* 5. 注意这里用的是LinkedHashMap,保证排序参数的顺序.如果是HashMap,排序参数的顺序可能会丢失,甚至反过来* 6. 依赖了hutool的ReflectUtil,后续尽量移除此依赖* </pre>** @author yi qiang* @date 2024-07-23 16:28:52*/
@SuppressWarnings("all")
public class SortMap extends LinkedHashMap<String, String> {public Comparator compartor() {if (this.isEmpty()) {return null;}Comparator comparator = null;// 遍历排序参数,构建comparator// 可以用reduce,但是感觉还没三元表达式清晰for (Map.Entry<String, String> entry : this.entrySet()) {comparator = (comparator == null) ?getTempComparator(entry.getKey(), entry.getValue()) ://多字段排序,第二个字段开始就要使用thenComparingcomparator.thenComparing(getTempComparator(entry.getKey(), entry.getValue()));}return comparator;}private Comparator getTempComparator(String fieldName, String direction) {if ("descend".equalsIgnoreCase(direction) || "desc".equalsIgnoreCase(direction)) {return Comparator.comparing(getKeyExtractor(fieldName), Comparator.reverseOrder());}return Comparator.comparing(getKeyExtractor(fieldName));}/*** 排序属性值的映射函数*/private Function getKeyExtractor(String fieldName) {return ele -> {Object o = ReflectUtil.getFieldValue(ele, fieldName);if (o instanceof Comparable) {return (Comparable) o;}throw new RuntimeException("The field " + fieldName + " does not implement Comparable interface");};}// 暂时未经过严格测试,比如父类属性,先不处理// private static Object getFieldValue(Object ele,String fieldName) {// try {// Field declaredField = ele.getClass().getDeclaredField(fieldName);// declaredField.setAccessible(true);// return declaredField.get(ele);// } catch (Exception ex) {// return null;// }// }public static void main(String[] args) {List<Test> tests = new ArrayList<>();Test test = new Test();test.setL(1);test.setDate(new Date());test.setS("c");tests.add(test);Test test1 = new Test();test1.setL(2);test1.setDate(new Date(System.currentTimeMillis() - 1000 * 60 * 60 * 24));test1.setS("a");tests.add(test1);Test test2 = new Test();test2.setL(2);test2.setDate(new Date(System.currentTimeMillis() - 1000 * 60 * 60 * 24));test2.setS("b");test2.setTest2(new Test2());tests.add(test2);SortMap sortMap = new SortMap();sortMap.put("l", "descend");sortMap.put("s", "descend");//测试Comparable接口未实现//sortMap.put("test2", "ascend");tests.stream().sorted(sortMap.compartor()).forEach(System.out::println);}@Datapublic static class Test {private long l;private String s;private Date date;private Test2 test2;}@Datapublic static class Test2 {private long l;private String s;}
}
运行结果
如果此工具能帮到大家,请动动手点个赞.