List的多维度排序案例演示~

关于List的多维度排序

日常工作中有很多关于list的排序功能, 看到公司封装的一个比较好的工具类, 这里拿来记录学习下。

public class SortBuilder<T> {

    // Never make these public
    static final int LEFT_IS_GREATER = 1;
    static final int RIGHT_IS_GREATER = -1;
    private static final Logger log = LoggerFactory.getLogger(SortBuilder.class);
    private List<SortFiled<T>> sortFileds = Lists.newLinkedList();
    private Map<String, Method> propertyMethodMap = null;
    private boolean nullsFirst;
    private boolean nullsLast;
    private boolean zerosFirst;
    private boolean zerosLast;
    private boolean natural;
    private Direction naturalDirection;

    private SortBuilder() {

    }

    public static <C> SortBuilder<C> newBuilder(Class clazz) {
        SortBuilder<C> sortBuilder = new SortBuilder<>();
        PropertyDescriptor[] propertyDescriptor = BeanUtils.getPropertyDescriptors(clazz);
        Map<String, Method> propertyMethodMap = new HashMap<>();
        for (PropertyDescriptor pd : propertyDescriptor) {
            String key = pd.getName();
            Method value = pd.getReadMethod();
            propertyMethodMap.put(key, value);
        }
        sortBuilder.propertyMethodMap = propertyMethodMap;
        return sortBuilder;
    }

    /**
     * null 排序到最前面
     */
    public SortBuilder<T> nullsFirst() {
        nullsFirst = true;
        nullsLast = false;
        return this;
    }

    /**
     * null 排序到最后面
     */
    public SortBuilder<T> nullsLast() {
        nullsFirst = false;
        nullsLast = true;
        return this;
    }

    /**
     * 0 排序到最后面
     */
    public SortBuilder<T> zerosLast() {
        zerosFirst = false;
        zerosLast = true;
        return this;
    }

    /**
     * 0 排序到最前面
     */
    public SortBuilder<T> zerosFirst() {
        zerosFirst = true;
        zerosLast = false;
        return this;
    }

    /**
     * 自然排序 {1,5,3,2,4} = {1,2,3,4,5}
     */
    public SortBuilder<T> natural() {
        natural = true;
        return this;
    }

    /**
     * 自然排序 {1,5,3,2,4} = {1,2,3,4,5}
     */
    public SortBuilder<T> natural(Direction direction) {
        natural = true;
        naturalDirection = direction;
        return this;
    }

    public SortBuilder<T> clear() {
        sortFileds.clear();
        return this;
    }

    /**
     * 增加一个降序
     *
     * @param fieldName 属性
     */
    public SortBuilder<T> addDesc(String fieldName) {
        addFieldMethod(fieldName, Direction.DESC);
        return this;
    }

    /**
     * 增加一个自定义排序
     *
     * @param comparator 自定义比较器
     */
    public SortBuilder<T> addCustom(Comparator<T> comparator) {
        sortFileds.add(SortFiled.builder(Direction.CUSTOM, null, comparator));
        return this;
    }


    /**
     * 增加一个升序
     *
     * @param fieldName 属性
     */
    public SortBuilder<T> addAsc(String fieldName) {
        addFieldMethod(fieldName, Direction.ASC);
        return this;
    }

    /**
     * 增加一个字段排序模式
     *
     * @param fieldName 属性
     * @param direction 排序方式
     */
    private SortBuilder<T> addFieldMethod(String fieldName, Direction direction) {
        Method method = propertyMethodMap.get(fieldName);
        if (method == null) {
            log.error("NoSuchMethodException field:{}", fieldName);
        } else {
            sortFileds.add(SortFiled.<T>builder(direction, method, null));
        }
        return this;
    }

    public SortBuilder<T> sortList(List<T> list) {
        Collections.sort(list, buildComparator());
        return this;
    }

    private Comparator<T> buildComparator() {
        return new Comparator<T>() {
            public int compare(T o1, T o2) {
                int flag = 0;
                if (sortFileds.size() == 0 && natural) {
                    flag = compareNatural(o1, o2, naturalDirection);
                } else {
                    for (SortFiled<T> sortFiled : sortFileds) {
                        Method method = sortFiled.getMethod();
                        Direction direction = sortFiled.getDirection();
                        if (direction == Direction.CUSTOM) {
                            flag = sortFiled.getComparator().compare(o1, o2);
                        } else {
                            flag = this.compare(method, o1, o2, direction);
                        }
                        if (flag != 0) {
                            break;
                        }
                    }
                }
                return flag;
            }

            private int compare(Method method, T left, T right, Direction direction) {
                int flag = 0;
                try {
                    Object leftVal = method.invoke(left);
                    Object rightVal = method.invoke(right);
                    Class returnType = method.getReturnType();
                    if (nullsFirst) {
                        flag = compareNulls(returnType, leftVal, rightVal, false, direction);
                    } else if (nullsLast) {
                        flag = compareNulls(returnType, rightVal, leftVal, true, direction);
                    } else {
                        flag = compare(returnType, leftVal, rightVal, direction);
                    }
                } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
                    e.printStackTrace();
                }
                return flag;
            }

            private int compareNatural(Object leftVal, Object rightVal, Direction direction) {
                int flag;
                if (nullsFirst) {
                    flag = compareNulls(leftVal, rightVal, false, direction);
                } else if (nullsLast) {
                    flag = compareNulls(rightVal, leftVal, true, direction);
                } else {
                    flag = compare(leftVal.getClass(), leftVal, rightVal, direction);
                }
                return flag;
            }

            private int compareNulls(Object leftVal, Object rightVal, boolean reverse, Direction direction) {
                return compareNulls(leftVal == null ? null : leftVal.getClass(), leftVal, rightVal, reverse, direction);
            }

            private int compareNulls(Class returnType, Object leftVal, Object rightVal, boolean reverse,
                                     Direction direction) {
                if (leftVal == rightVal) {
                    return 0;
                } else if (leftVal == null) {
                    return RIGHT_IS_GREATER;
                } else if (rightVal == null) {
                    return LEFT_IS_GREATER;
                } else {
                    return reverse ? compare(returnType, rightVal, leftVal, direction) : compare(returnType, leftVal,
                            rightVal, direction);
                }
            }

            private int compareZeros(BigDecimal leftNumber, BigDecimal rightNumber, boolean reverse, Direction direction) {
                if (leftNumber.compareTo(rightNumber) == 0) {
                    return 0;
                } else if (leftNumber.compareTo(BigDecimal.valueOf(0)) == 0) {
                    return RIGHT_IS_GREATER;
                } else if (rightNumber.compareTo(BigDecimal.valueOf(0)) == 0) {
                    return LEFT_IS_GREATER;
                } else {
                    return reverse ? compareNumber(rightNumber, leftNumber, direction) : compareNumber(leftNumber,
                            rightNumber, direction);
                }
            }

            private int compareNumber(BigDecimal leftNumber, BigDecimal rightNumber, Direction direction) {
                if (leftNumber.compareTo(rightNumber) == 0) {
                    return 0;
                } else {
                    return direction == null || direction == Direction.ASC ? leftNumber.compareTo(rightNumber)
                            : rightNumber.compareTo(leftNumber);
                }
            }

            private int compare(Class returnType, Object leftVal, Object rightVal, Direction direction) {
                int flag;
                if (Number.class.isAssignableFrom(returnType) || returnType == int.class || returnType == long.class
                        || returnType == double.class || returnType == float.class) {
                    BigDecimal leftNumber = BigDecimal.valueOf(Double.valueOf(leftVal.toString()));
                    BigDecimal rightNumber = BigDecimal.valueOf(Double.valueOf(rightVal.toString()));
                    if (zerosFirst) {
                        flag = compareZeros(leftNumber, rightNumber, false, direction);
                    } else if (zerosLast) {
                        flag = compareZeros(rightNumber, leftNumber, true, direction);
                    } else {
                        flag = compareNumber(leftNumber, rightNumber, direction);
                    }
                } else {
                    String methodReturn1 = leftVal.toString();
                    String methodReturn2 = rightVal.toString();
                    if (direction == null || direction == Direction.ASC) {
                        flag = methodReturn1.compareTo(methodReturn2);
                    } else {
                        flag = methodReturn2.compareTo(methodReturn1);
                    }
                }
                return flag;
            }
        };
    }

    /**
     * 排序方式:
     * <p/>
     * ASC:升序,DESC:降序
     */
    public enum Direction {
        ASC, DESC, CUSTOM
    }

    static class SortFiled<T> {
        private Method method;
        private Direction direction;
        private Comparator<T> comparator;


        SortFiled(Direction direction, Method method, Comparator<T> comparator) {
            this.method = method;
            this.direction = direction;
            this.comparator = comparator;
        }

        @SuppressWarnings("unchecked")
        public static <T> SortFiled<T> builder(Direction direction, Method method, Comparator<T> comparator) {
            return new SortFiled(direction, method, comparator);
        }

        public Method getMethod() {
            return method;
        }

        public void setMethod(Method method) {
            this.method = method;
        }

        public Direction getDirection() {
            return direction;
        }

        public void setDirection(Direction direction) {
            this.direction = direction;
        }

        public Comparator<T> getComparator() {
            return comparator;
        }

        public void setComparator(Comparator<T> comparator) {
            this.comparator = comparator;
        }
    }
}

使用方法:

@Autowired
private SeriesDiagramEntityService seriesDiagramEntityService;
@org.junit.Test
public void testSortBuilder() throws Exception{
    List<SeriesDiagramEntity> list = seriesDiagramEntityService.findAll(SeriesDiagramEntity.Fields.status.eq(CommonConstants.DataStatus.PUBLISH).limit(1000));
    SortBuilder.<SeriesDiagramEntity>newBuilder(SeriesDiagramEntity.class).addAsc("source").addAsc("displayOrder").addDesc("status").sortList(list);
}

使用起来很简单,我们这里写了个unit test来演示。 原理就是直接使用JavaBean中的属性进行asc或者desc的排序展示。

下面来看看它是具体怎么运行的:

  • 首先获取到实体类的属性, 然后添加到propertyMethodMap中。 PropertyDescriptor[] propertyDescriptor = BeanUtils.getPropertyDescriptors(clazz);
  • 然后是将用户自定义的排序字段加入到sortFileds中去:
/**
 * 增加一个升序
 *
 * @param fieldName 属性
 */
public SortBuilder<T> addAsc(String fieldName) {
    addFieldMethod(fieldName, Direction.ASC);
    return this;
}
/**
 * 增加一个字段排序模式
 *
 * @param fieldName 属性
 * @param direction 排序方式
 */
private SortBuilder<T> addFieldMethod(String fieldName, Direction direction) {
    Method method = propertyMethodMap.get(fieldName);
    if (method == null) {
        log.error("NoSuchMethodException field:{}", fieldName);
    } else {
        sortFileds.add(SortFiled.<T>builder(direction, method, null));
    }
    return this;
}
  • 进行排序
public SortBuilder<T> sortList(List<T> list) {
    Collections.sort(list, buildComparator());
    return this;
}

private Comparator<T> buildComparator() {
    return new Comparator<T>() {
        public int compare(T o1, T o2) {
            int flag = 0;
            if (sortFileds.size() == 0 && natural) {
                flag = compareNatural(o1, o2, naturalDirection);
            } else {
                for (SortFiled<T> sortFiled : sortFileds) {
                    Method method = sortFiled.getMethod();
                    Direction direction = sortFiled.getDirection();
                    if (direction == Direction.CUSTOM) {
                        flag = sortFiled.getComparator().compare(o1, o2);
                    } else {
                        flag = this.compare(method, o1, o2, direction);
                    }
                    if (flag != 0) {
                        break;
                    }
                }
            }
            return flag;
        }

        private int compare(Method method, T left, T right, Direction direction) {
            int flag = 0;
            try {
                Object leftVal = method.invoke(left);
                Object rightVal = method.invoke(right);
                Class returnType = method.getReturnType();
                if (nullsFirst) {
                    flag = compareNulls(returnType, leftVal, rightVal, false, direction);
                } else if (nullsLast) {
                    flag = compareNulls(returnType, rightVal, leftVal, true, direction);
                } else {
                    flag = compare(returnType, leftVal, rightVal, direction);
                }
            } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
            return flag;
        }

        private int compareNatural(Object leftVal, Object rightVal, Direction direction) {
            int flag;
            if (nullsFirst) {
                flag = compareNulls(leftVal, rightVal, false, direction);
            } else if (nullsLast) {
                flag = compareNulls(rightVal, leftVal, true, direction);
            } else {
                flag = compare(leftVal.getClass(), leftVal, rightVal, direction);
            }
            return flag;
        }

        private int compareNulls(Object leftVal, Object rightVal, boolean reverse, Direction direction) {
            return compareNulls(leftVal == null ? null : leftVal.getClass(), leftVal, rightVal, reverse, direction);
        }

        private int compareNulls(Class returnType, Object leftVal, Object rightVal, boolean reverse,
                                 Direction direction) {
            if (leftVal == rightVal) {
                return 0;
            } else if (leftVal == null) {
                return RIGHT_IS_GREATER;
            } else if (rightVal == null) {
                return LEFT_IS_GREATER;
            } else {
                return reverse ? compare(returnType, rightVal, leftVal, direction) : compare(returnType, leftVal,
                        rightVal, direction);
            }
        }

        private int compareZeros(BigDecimal leftNumber, BigDecimal rightNumber, boolean reverse, Direction direction) {
            if (leftNumber.compareTo(rightNumber) == 0) {
                return 0;
            } else if (leftNumber.compareTo(BigDecimal.valueOf(0)) == 0) {
                return RIGHT_IS_GREATER;
            } else if (rightNumber.compareTo(BigDecimal.valueOf(0)) == 0) {
                return LEFT_IS_GREATER;
            } else {
                return reverse ? compareNumber(rightNumber, leftNumber, direction) : compareNumber(leftNumber,
                        rightNumber, direction);
            }
        }

        private int compareNumber(BigDecimal leftNumber, BigDecimal rightNumber, Direction direction) {
            if (leftNumber.compareTo(rightNumber) == 0) {
                return 0;
            } else {
                return direction == null || direction == Direction.ASC ? leftNumber.compareTo(rightNumber)
                        : rightNumber.compareTo(leftNumber);
            }
        }

        private int compare(Class returnType, Object leftVal, Object rightVal, Direction direction) {
            int flag;
            if (Number.class.isAssignableFrom(returnType) || returnType == int.class || returnType == long.class
                    || returnType == double.class || returnType == float.class) {
                BigDecimal leftNumber = BigDecimal.valueOf(Double.valueOf(leftVal.toString()));
                BigDecimal rightNumber = BigDecimal.valueOf(Double.valueOf(rightVal.toString()));
                if (zerosFirst) {
                    flag = compareZeros(leftNumber, rightNumber, false, direction);
                } else if (zerosLast) {
                    flag = compareZeros(rightNumber, leftNumber, true, direction);
                } else {
                    flag = compareNumber(leftNumber, rightNumber, direction);
                }
            } else {
                String methodReturn1 = leftVal.toString();
                String methodReturn2 = rightVal.toString();
                if (direction == null || direction == Direction.ASC) {
                    flag = methodReturn1.compareTo(methodReturn2);
                } else {
                    flag = methodReturn2.compareTo(methodReturn1);
                }
            }
            return flag;
        }
    };
}

这里排序规则比较多, 大概就是这样。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏菩提树下的杨过

数据结构C#版笔记--顺序表(SeqList)

线性结构(Linear Stucture)是数据结构(Data Structure)中最基本的结构,其特征用图形表示如下: ? 即:每个元素前面有且只有一个元...

2119
来自专栏cs

c#知识点2.0 语句

主要说一下我认为比较重要的东西。 foreach循环 格式: foreach(类型 循环变量 in 变量的取值集合) 变量值集合类型必须是一个集合,而循环变...

3506
来自专栏技术博客

Asp.net MVC后台 XML、DataTable、DataSet之间的数据转换

  上面的方法只是将XMl字符串读入到DataSet中,然后再冲DataSet中查找先前定义过的DataTable即可。

1172
来自专栏大内老A

由for V.S. for each想到的

一直想写一系列如何提高Performance和Scalability的文章,把我的相关经验和所知道的相关的技巧同大家分享。前一阵在园子里有一篇讨论for eac...

1888
来自专栏吴伟祥

按层级/条件解析Json,获取相应的key或value中的相关数据

1792
来自专栏程序你好

理解C#语言中相等Equality 和唯一 Identity

1002
来自专栏我是业余自学C/C++的

扩展的线性表

2333
来自专栏林德熙的博客

C# 枚举转字符串 枚举转字符串字符串转枚举

如果把一个枚举转字符串,那么如何把字符串转枚举?可以使用 Enum.Parse 不过这个方法可以会抛异常,所以使用需要知道字符串是可以转

2481
来自专栏跟着阿笨一起玩NET

嘿,原来不认识你,想不到你这么好用—说说.NET中被我忽视的方法

下面就说说被我忽视过的方法。当然,每个人的编程经历,涉猎面及对.NET的认知程度都不一样。所以,这只是一家之言,肯定有很多不足之处,欢迎大家批评指正。

691
来自专栏Java后端技术

22中编程语言的HelloWorld

731

扫码关注云+社区

领取腾讯云代金券