背景
对于排序而言,比较常见的场景是前端传递所需的排序字段名和排序方向,然后通过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()) :
//多字段排序,第二个字段开始就要使用thenComparing
comparator.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);
}
@Data
public static class Test {
private long l;
private String s;
private Date date;
private Test2 test2;
}
@Data
public static class Test2 {
private long l;
private String s;
}
}
运行结果
如果此工具能帮到大家,请动动手点个赞.