字段排序器

/**
 * 字段排序器<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;
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值