/**
* 字段排序器<br>
* 将数组以数组中的对象的某些字段排序<br>
* 支持多字段组合排序, 支持升序,降序<br>
* 对象的排序字段只支持已实现Comparable接口的类型, 且不支持数组<br>
* <pre>
* // 按年龄升序,姓名降序排序
* new FieldSorter().asc("age").desc("name").sort(array);
* </pre>
* @author zhaohuihua
*/
public class FieldSorter {
/** 升序 **/
public static final int ASC = 1;
/** 降序 **/
public static final int DESC = 2;
/** 排序时的字段比较器 **/
private FieldComparator comparator;
/** 排序字段 **/
private ArrayList sortFields;
/** 缓存对象 **/
private HashMap map;
/** 构造函数 **/
public FieldSorter() {
comparator = new FieldComparator();
}
/**
* 增加升序排序字段
* @param fieldName 字段名
*/
public void addAscSortField(String fieldName) {
sortFields.add(new SortField(fieldName, ASC));
}
/**
* 增加降序排序字段
* @param fieldName 字段名
*/
public void addDescSortField(String fieldName) {
sortFields.add(new SortField(fieldName, DESC));
}
/**
* 增加升序排序字段<br>
* 是addAscSortField的简化形式, 并返回排序器本身, 便于连写
* @param fieldName 字段名
* @return 返回排序器本身, 便于连写
*/
public FieldSorter asc(String fieldName) {
addAscSortField(fieldName);
return this;
}
/**
* 增加降序排序字段<br>
* 是addDescSortField的简化形式, 并返回排序器本身, 便于连写
* @param fieldName 字段名
* @return 返回排序器本身, 便于连写
*/
public FieldSorter desc(String fieldName) {
addDescSortField(fieldName);
return this;
}
/**
* 执行排序操作
* @param array 等排序的数组
*/
public void sort(Object[] array) {
Arrays.sort(array, comparator);
}
/**
* 字段比较器<br>
* 支持多字段组合比较, 支持升序,降序<br>
* 对象的比较字段只支持已实现Comparable接口的类型, 且不支持数组
*/
private class FieldComparator implements Comparator {
/** 构造函数 **/
public FieldComparator() {
map = new HashMap();
sortFields = new ArrayList();
}
/**
* 比较两个对象是否相同<br>
* 如果两个对象都为null, 返回true
*/
private boolean equals(Object a, Object b) {
if(a == null && b == null) {
return true;
} else if(a != null) {
return a.equals(b);
} else {
return b.equals(a);
}
}
/** 执行比较 **/
public int compare(Object o1, Object o2) {
if(sortFields.size() == 0) return 0;
Iterator iterator = sortFields.iterator();
while(iterator.hasNext()) {
SortField sortField = (SortField)iterator.next();
String fieldName = sortField.getFieldName();
int sortType = sortField.getSortType();
try {
Object v1 = getField(o1, fieldName);
Object v2 = getField(o2, fieldName);
// 当前字段值相同, 比较下一字段
if(equals(v1, v2)) continue;
// 同时为空的情况已经通过equals()排除了
if(v1 == null) {
return sortType == DESC ? 1 : -1;
} else if(v2 == null) {
return sortType == DESC ? -1 : 1;
} else if(Comparable.class.isInstance(v1)) {
int result = ((Comparable)v1).compareTo(v2);
if(sortType == DESC) result = - result;
return result;
} else {
return 0;
}
} catch (Exception e) {
return 0;
}
}
return 0;
}
/**
* 获取对象的字段值<br>
* 最初的实现是通过异常判断字段是否存在<br>
* <pre>
* try {
* clazz.getDeclaredField(fieldName);
* } catch(NoSuchFieldException e) {
* }
* </pre>
* 自身字段性能还可以<br>
* 10000行数据, 自身字段, 耗时: 00:00:00.312<br>
* 但继承字段性能奇差<br>
* 10000行数据, 继承字段, 耗时: 00:00:01.531<br>
* 改为遍历后性能有很大的提升<br>
* 10000行数据, 继承字段, 耗时: 00:00:00.609<br>
* <br>
* 利用缓存保存class的字段列表后性能进一步提升<br>
* 10000行数据, 自身字段, 耗时: 00:00:00.125<br>
* 10000行数据, 继承字段, 耗时: 00:00:00.187<br>
* @param obj
* @param fieldName
* @return
* @throws Exception
*/
protected Object getField(Object obj, String fieldName)
throws Exception {
if(obj == null) return null;
if(fieldName == null) throw new NullPointerException("fieldName");
Class clazz = obj.getClass();
while(clazz != null) {
Field[] fields;
if(map.containsKey(clazz)) {
fields = (Field[])map.get(clazz);
} else {
fields = clazz.getDeclaredFields();
map.put(clazz, fields);
}
//
for(int i = 0; i < fields.length; i++) {
Field field = fields[i];
if(field.getType().isArray()) {
String msg = fieldName + " field type is array";
throw new IllegalArgumentException(msg);
}
// 排除静态字段
if(Modifier.isStatic(field.getModifiers())) continue;
// 排除与声明的字段名不相符的字段
if(!fields[i].getName().equals(fieldName)) continue;
field.setAccessible(true);
return field.get(obj);
}
clazz = clazz.getSuperclass(); // 循继承树上溯
}
throw new NoSuchFieldException(fieldName);
}
}
/** 排序字段 **/
private static class SortField {
/** 字段名 **/
private final String fieldName;
/** 排序类型: ASC.升序|DESC.降序 **/
private final int sortType;
public SortField(String fieldName, int sortType) {
this.fieldName = fieldName;
this.sortType = sortType;
}
/** 获取字段名 **/
public String getFieldName() {
return fieldName;
}
/** 获取排序类型: ASC.升序|DESC.降序 **/
public int getSortType() {
return sortType;
}
}
}