实现之路: 2.1 从泛型是获取不了javabean的class对象的,取巧了一下,用javabean的实例来getClass() 2.2 尝试了用Field.get(Object)来获取javabean的field,后来实验出来,借Method.invode()来调用javabean一般都有的getter()函数稍快一点。到百万级的调用能有几百毫秒的差别。 2.3 主要还是调用Collections.sort(list, Comparator)方法来排序,发现竟然比直接用经典的直接写闭包Comparator慢了10倍。简直不能人,遂寻找性能优化的方法。 2.4 立竿见影的setAccessible(true)。反射调用/访问之所以慢,很大一部分原因是检查类型,检查能不能访问。设置了true后,关闭了对可访问性的检查。这样设置后,还能用Field.get()的方法访问的javabean的私有成员呢。 2.5 上面说到慢的一个原因还在于类型检查,参数装箱和封箱。业界有得做法是操作字节码,我感觉那样太复杂了,在纯java语言范围内,还找到了一个提升的点。在Collections.sort(list, Comparator)里面的compare函数,如果用反射来调用,起码需要对形参的两个Object取field,小计:两次getter调用;一次o1.comparaTo(o2)的调用,小计:一次compareTo调用。sort函数的一次比较就要3次Method.invoke(), 虽然免去了访问检查,反射调用还是比较慢。 2.6 MethodHandle.invokeExact(),用更加轻量级的调用方法,这个比Method.invoke()更轻量,更快。原因在于前者在调用前,返回值、参数列表和调用对象都必须从“符号上”确定,自然就免去了类型检查的时间。什么叫“符号上”呢? 比方说 你知道invokeExact作用对象是String, 你不能 String.class.cast(Object_instance)来放入invokeExact. 有鉴于此,只有 compareTo函数是可以用invokeExact的, 返回值是确定的int, 作用对象是Comparable,参数是Comparable或Object。 getter函数就不行,getter返回的不知道是什么东东。 2.7 Talk is cheap, show me the code! code in github
/**
* sort a list containing JavaBean according specific key( field ).
* Mostly, sortByField take ~1.5 time 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
* @author Tony
* @email 360517703@163.com
* @Time 2015-08-14 11:12
*/publicstatic <E> voidsortByField(List<E> list, String fieldName, String order) {
if (list == null || list.size() < 2) { // no need to sortreturn;
}
if (fieldName == null || fieldName.trim().equals(""))
return; // won't sort if fieldName is null or ""// get actual class of generic E
Class<?> eClazz = null; // use reflect to get the actual classboolean isAllNull = true; // default all elements are nullfor (E e : list) {
if (e != null) {
isAllNull = false;
eClazz = e.getClass();
break;
}
}
if (isAllNull)
return; // no need to sort, because all elements are null// check fieldName in Class E
Field keyField = null; // the <fieldName> Field as sort keytry {
keyField = eClazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException e1) {
e1.printStackTrace();
System.out.println("The List<E> doesn't contain fieldName. That is "
+ String.format("%s has no Field %s.", eClazz, fieldName));
} catch (SecurityException e1) {
e1.printStackTrace();
System.out.println("deny access to class or field.");
}
// check field is either Comparable
Class<?> fieldClazz = keyField.getType();
boolean isComparable = Comparable.class.isAssignableFrom(fieldClazz);
if (isComparable == false)
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 methodif (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; // change the first char to lowerCase
getterName.append(cs);
Method getterMethod = null;
try {
getterMethod = eClazz.getDeclaredMethod(getterName.toString());
} catch (NoSuchMethodException | SecurityException e1) {
// System.out.println("Field " + fieldName + " has no " + getterName// + "() . ");// e1.printStackTrace();
}
/*
* // get compare method for specified field. //Abandoned. Because
* MethodHandle.invokeExact() is a little faster than Method.invoke()
* Method cmpMethod = null; try { cmpMethod =
* fieldClazz.getDeclaredMethod("compareTo", fieldClazz); } catch
* (NoSuchMethodException | SecurityException e1) { System.out.println(
* "deny access to class or method(comparaTo).\nImpossible to show errorStrackTrace Because of Comparable check"
* ); e1.printStackTrace(); cmpMethod.setAccessible(true); }
*/
Cmp<E> cmp;
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodType type = MethodType.methodType(int.class, Object.class);
MethodHandle mh = null;
try {
mh = lookup.findVirtual(Comparable.class, "compareTo", type);
} catch (NoSuchMethodException | IllegalAccessException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
if (getterMethod != null) {
// cmpMethod.setAccessible(true);
getterMethod.setAccessible(true);
cmp = new Cmp<E>(mh, getterMethod);
} else { // if cannot find getter method, use reflect to get specified// field
keyField.setAccessible(true);
cmp = new Cmp<E>(mh, keyField);
}
if (order.equalsIgnoreCase("desc")) {
Collections.sort(list, Collections.reverseOrder(cmp));
return;
}
Collections.sort(list, cmp);
}