- @Slf4j
- public class SortByField {
-
- static class Cmp
implements Comparator { - 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
void sortByField(List 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
Field as sort key - try {
- keyField = eClazz.getDeclaredField(fieldName);
- } catch (NoSuchFieldException e1) {
- log.error(String.format("The List
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
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.
- *
- * 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
void sortByField(List list, String fieldName) { - sortByField(list, fieldName, "asc");
- }
-
- }
-
- // Collections.reverseOrder(Comparator cmp)源码:
- public static
Comparator reverseOrder(Comparator cmp) { - if (cmp == null)
- return reverseOrder();
-
- if (cmp instanceof ReverseComparator2)
- return ((ReverseComparator2
)cmp).cmp; -
- return new ReverseComparator2<>(cmp);
- }
- // ((ReverseComparator2
)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
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