@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