List对象集合按照对象某一属性字段排序

@Slf4j
public class SortByField {

    static class Cmp<E> implements Comparator<E> {
        Method getMethod = null;
        Field fieldToGet = null;
        MethodHandle cmpMethodHandle;

        Cmp(MethodHandle cmpMethodHandle, Method getMethod) {
            this.getMethod = getMethod;
            this.cmpMethodHandle = cmpMethodHandle;
        }

        Cmp(MethodHandle cmpMethodHandle, Field fieldToGet) {
            this.cmpMethodHandle = cmpMethodHandle;
            this.fieldToGet = fieldToGet;
        }

        @Override
        public int compare(E o1, E o2) {
            if (o1 == null && o2 == null) {
                return 0;
            }
            if (o1 == null) {
                return -1;
            }
            if (o2 == null) {
                return 1;
            }
            try {
                Object o1GetMethod = getMethod.invoke(o1);
                Object o2GetMethod = getMethod.invoke(o2);
                if (o1GetMethod == null && o2GetMethod == null) {
                    return 0;
                }
                if (o1GetMethod == null) {
                    return -1;
                }
                if (o2GetMethod == null) {
                    return 1;
                }
                log.info(o1GetMethod.toString().concat("-").concat(o2GetMethod.toString()));
                if (getMethod != null) {
                    return (int) cmpMethodHandle.invokeExact((Comparable<?>) o1GetMethod,
                            o2GetMethod);
                }
                if (fieldToGet != null) {
                    return (int) cmpMethodHandle.invokeExact((Comparable<?>) fieldToGet.get(o1), fieldToGet.get(o2));
                }
            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                log.error("If sortByField() isn't modifier, it won't print errorStackTrace. Default return 0", e);
            } catch (Throwable e) {
                log.error("unknown error...", e);
            }
            return 0;
        }
    }

    /**
     * sort a list containing JavaBean according specific key( field ).
     * Mostly, sortByField take ~1.5 times as much as Traditional implementation when list.size() > 100K
     *
     * @param list:      list to be sorted
     * @param fieldName: sort list according this field
     * @param order:     asc(default) or desc
     */
    public static <E> void sortByField(List<E> list, String fieldName, String order) {
        if (list == null || list.size() < 2) { // no need to sort
            return;
        }
        if (fieldName == null || fieldName.trim().isEmpty())
            // won't sort if fieldName is null or ""
            return;
        // get actual class of generic E
        Class<?> eClazz = null; // use reflect to get the actual class
        boolean isAllNull = true; // default all elements are null
        for (E e : list) {
            if (e != null) {
                isAllNull = false;
                eClazz = e.getClass();
                break;
            }
        }
        if (isAllNull)
            // no need to sort, because all elements are null
            return;
        // check fieldName in Class E
        Field keyField; // the <fieldName> Field as sort key
        try {
            keyField = eClazz.getDeclaredField(fieldName);
        } catch (NoSuchFieldException e1) {
            log.error(String.format("The List<E> doesn't contain fieldName. That is %s has no Field %s.", eClazz, fieldName), e1);
            throw new Exception(String.format("列表不包含字段名称. That is %s has no Field %s.", eClazz, fieldName));
        } catch (SecurityException e1) {
            log.error("deny access to  class or field.", e1);
            throw new Exception("拒绝访问类或字段");
        }
        // check field is either Comparable
        Class<?> fieldClazz = keyField.getType();
        boolean isComparable = Comparable.class.isAssignableFrom(fieldClazz);
        if (!isComparable)
            return; // if the class of fieldName is not comparable, don't sort
        // try to use getter method to get field first. Because a little faster
        // than Field.get(Object)
        StringBuilder getterName; // adapt to JavaBean getter method
        if (fieldClazz.getSimpleName().equals("Boolean")) {
            getterName = new StringBuilder("is");
        } else {
            getterName = new StringBuilder("get");
        }
        // 将第一个字符更改为大写
        /*
        char[] cs = fieldName.toCharArray();
        if (cs[0] >= 'a' && cs[0] <= 'z')
            cs[0] -= 32;
        getterName.append(cs);
        */
        String capitalizeFieldName = StringUtils.capitalize(fieldName);
        getterName.append(capitalizeFieldName);
        Method getterMethod;
        try {
            getterMethod = eClazz.getDeclaredMethod(getterName.toString());
        } catch (NoSuchMethodException | SecurityException e1) {
            log.error("Field " + fieldName + " has no " + getterName + "() . ", e1);
            throw new Exception("Field " + fieldName + " has no " + getterName + "() . ");
        }

        Cmp<E> cmp;
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodType type = MethodType.methodType(int.class, Object.class);
        MethodHandle mh;
        try {
            mh = lookup.findVirtual(Comparable.class, "compareTo", type);
        } catch (NoSuchMethodException | IllegalAccessException e1) {
            throw new Exception(String.format("未知的排序字段:%s", fieldName));
        }
        if (getterMethod != null) {
            getterMethod.setAccessible(true);
            cmp = new Cmp<>(mh, getterMethod);
        } else {
            // if cannot find getter method, use reflect to get specified
            // field
            keyField.setAccessible(true);
            cmp = new Cmp<>(mh, keyField);
        }

        if (order.equalsIgnoreCase("desc")) {
            list.sort(Collections.reverseOrder(cmp));
            return;
        }
        list.sort(cmp);
    }

    /**
     * sort a list containing JavaBean according specific key( field ) order by
     * ascend.
     * <p>
     * Mostly, sortByField take ~1.5 times as much as Traditional implementation when list.size() > 100K
     *
     * @param list      list to be sort
     * @param fieldName sort list according this field
     */
    public static <E> void sortByField(List<E> list, String fieldName) {
        sortByField(list, fieldName, "asc");
    }

}

// Collections.reverseOrder(Comparator cmp)源码:
public static <T> Comparator<T> reverseOrder(Comparator<T> cmp) {
    if (cmp == null)
        return reverseOrder();

    if (cmp instanceof ReverseComparator2)
        return ((ReverseComparator2<T>)cmp).cmp;

    return new ReverseComparator2<>(cmp);
}
// ((ReverseComparator2<T>)cmp).cmp compare方法
public int compare(T t1, T t2) {
    return cmp.compare(t2, t1);
}

从Collections.reverseOrder(Comparator cmp)源码中可以看出,在执行Compartor比较器的重写方法compare(Obiect o1, Object o2)前,会根据排序方式(升序/降序),将o1和o2进行不同的赋值

// 测试类
@NoArgsConstructor
@AllArgsConstructor
@Data
class strClass {
    String name;
}

@Test
void sortTest() {
    List<strClass> stringList = Lists.newArrayList(new strClass("aaa"),
            new strClass("bbb"));

    SortByField.sortByField(stringList, "name");
    System.out.println("升序结果:" + stringList.stream().map(strClass::getName).collect(Collectors.toList()));
    SortByField.sortByField(stringList, "name", "desc");
    System.out.println("降序结果:" + stringList.stream().map(strClass::getName).collect(Collectors.toList()));
}

测试结果:

16:53:44.911 [main] INFO xxx.SortByField - o1:bbb-o2:aaa
16:53:44.916 [main] INFO xxx.SortByField - Comparator override compare() return:1
升序结果:[aaa, bbb]
16:53:44.918 [main] INFO xxx.SortByField - o1:aaa-o2:bbb
16:53:44.918 [main] INFO xxx.SortByField - Comparator override compare() return:-1
降序结果:[bbb, aaa]

排序时调用重写的compartor的compare(Obiect o1, Object o2)方法,在compare方法里比较o1和o2;

从测试结果可以得出:

当o1大于o2时,返回1,当o1小于o2时,返回-1,当o1等于o2时,返回0;

当compare()返回值为-1时,则需要调整o1和o2的位置;

注意:

当对一个list{A,B}进行排序时:

  • 若为升序排序:在执行compare()方法时,会将A赋值给o2,将B赋值给o1;
  • 若为降序排序:在执行compare()方法时,会将A赋值给o1,将B赋值给o2;

源码参考:https://github.com/Tony36051/sortByField/blob/master/src/main/java/SortByField.java

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值