1. 程式人生 > >(小工具)Java反射機制實現任意物件集合排序並且獲取排列名次的通用程式實踐

(小工具)Java反射機制實現任意物件集合排序並且獲取排列名次的通用程式實踐

在應用開發的時候,我們有時候會遇到需要對一個給定集合進行排序的情況。如果該集合的資料記錄是從資料庫的獲取,通常我們最簡單的方案是直接在sql層面select後order by完成。 但是,假如某些場景下該資料不是從資料庫獲取呢?那就需要在應用程式層面進行排序。

假設給定一個集合,{5,4,3,6}, 需要對其進行排序,使其倒序排序後結果是 {6,5,4,3}。 實現方案之一可以使用Java集合框架提供的排序功能Collections.sort。

下面是簡單的例子:
/*  public void sort(List<TspPefScoreCalTotal> list){
        Comparator<TspPefScoreCalTotal> comparator = new
Comparator<TspPefScoreCalTotal>() { public int compare(TspPefScoreCalTotal s1, TspPefScoreCalTotal s2) { if (!s1.getTotalScore().equals(s2.getTotalScore())) { return Double.compare(s2.getTotalScore(), s1.getTotalScore()); // 從大到小排,3,2,1 } else
{ return 0; } } }; Collections.sort(list, comparator); }*/

但是,上面的例子就hardcode了List集合裡的物件是TspPefScoreCalTotal,這樣就使得程式不具有通用性。試試想,下一次我是對其它資料物件的某個欄位進行排序呢? 那豈不是重複寫上面相似的程式碼?

於是,嘗試使用java的反射機制寫一個通用程式,使其可以讓呼叫者輸入任意的物件,且可以任意指定按照哪個欄位進行排序。下面給出具體實現例子,並且例子裡實現了對排序後給出排列名次。

public class SortUtil<T> {

    String sortfield = "default";//
    String sortOrder = DESC;
    Class sortfieldTypeClass = null;
    public static String ASC = "ASC";//降序排序
    public static String DESC = "DESC";//升序排序

    /**
     * 對於已排序的集合,給出排列名次,對於相同名次的物件,下一個物件的名次是否連續
     * @param list        要進行給出排列名次的集合
     * @param objClazz    集合裡的物件
     * @param _sortField  按某欄位排序
     * @param _rankField  儲存排列名次的欄位,名字來源於實體類屬性
     * @param continu     相同名次的物件,下一個物件的名次是否連續
     */
    public void sortRank(List<T> list, Class objClazz, String _sortField, String _rankField, boolean continu){

        if (continu) { // 1,2,3,4,5 這樣連續排名
            int i = 1;
            for (T obj : list) {
                setValueByProperty(obj, _rankField, (long) i);
                i++;
            }
        }
        else          // 1,1,3,4,4,6 這樣不連續排名
        {
            int i = 1;
            T pre = null;
            for (T obj : list) {

                if(i==1){
                    setValueByProperty(obj,_rankField,1L);
                }else{

                    if(getFieldValueByName(_sortField, obj).equals(getFieldValueByName(_sortField, pre))){//當前和前一名排名相同
                        setValueByProperty(obj,_rankField, getFieldValueByName(_rankField, pre));//記錄前一個記錄的排名
                        i++;
                        continue;
                    }
                    setValueByProperty(obj,_rankField,(long)i);

                }
                i++;
                pre = obj;

            }
        }

    }

    /**
     * 給出某個list集合, 對集合裡的物件進行排序, 排序的欄位和排序順序可以配置
     * @param list        要進行排序的集合
     * @param _sortField  按某欄位排序
     * @param objClazz    集合裡的物件
     * @param _sortOrder  排序的順序
     */
    public void sortGeneral(List<T> list, String _sortField, Class objClazz, String _sortOrder) {
        sortfield = _sortField;
        sortfieldTypeClass = getFiledType(_sortField, objClazz);
        sortOrder = _sortOrder;
        if (!sortfieldTypeClass.getName().equals("java.lang.Long") && !sortfieldTypeClass.getName().equals("java.lang.Double")) {
            throw new RuntimeException("排序欄位型別要求是Long或者Double型別");
        }

        Comparator<T> comparator = new Comparator<T>() {
            public int compare(T s1, T s2) {

                if (getFieldValueByName(sortfield, s1) == null || getFieldValueByName(sortfield, s2) == null) {
                    throw new RuntimeException("比較欄位的值不能為空");
                }

                // 控制排序順序 start
                T first = null;
                T second = null;
                if (DESC.equals(sortOrder)) { // 從大到小排,3,2,1
                    first = s2;
                    second = s1;
                } else if (ASC.equals(sortOrder)) { // 從小到大排,1,2,3
                    first = s1;
                    second = s2;
                }
                // 控制排序順序 end

                if (!getFieldValueByName(sortfield, first).equals(getFieldValueByName(sortfield, second))) {
                    if (("java.lang.Double").equals(sortfieldTypeClass.getName())) {
                        return Double.compare((Double) getFieldValueByName(sortfield, first), (Double) getFieldValueByName(sortfield, second));
                    }
                    return Double.compare((Long) getFieldValueByName(sortfield, first), (Long) getFieldValueByName(sortfield, second));
                } else {
                    return 0;
                }
            }
        };

        Collections.sort(list, comparator);

    }

    /**
     * 根據屬性名獲取屬性值
     * */
    public Object getFieldValueByName(String fieldName, Object o) {
        try {
            String firstLetter = fieldName.substring(0, 1).toUpperCase();
            String getter = "get" + firstLetter + fieldName.substring(1);
            Method method = o.getClass().getMethod(getter, new Class[] {});
            Object value = method.invoke(o, new Object[] {});
            return value;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 根據屬性名獲取屬性型別
     * */
    public Class<?> getFiledType(String field, Class classParam) {
        Field[] fields = classParam.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            if(fields[i].getName().equals(field)){
                return fields[i].getType();
            }
        }
        return null;
    }

    /**
     * 物件任意屬性賦值
     * 物件要求有get,set方法
     */
    public Object setValueByProperty(Object objMain,String property,Object objValue) {
        try{

            Method[] arrayMethodsOut =objMain.getClass().getMethods();
            Map<String,Method> mapMethodsOut =new HashMap<String, Method>(); 
            for( int i=0;i<arrayMethodsOut.length;i++ ){
                mapMethodsOut.put(arrayMethodsOut[i].getName(), arrayMethodsOut[i]);
            }

            String sMethodNameSet = "set" + StringUtil.formatFirstUpper(property);
            Method methodTempSet =mapMethodsOut.get( sMethodNameSet );
            if( methodTempSet!=null ){
                try{
                    //避免系統的Set方法,影響其他屬性
                    methodTempSet.invoke(objMain, new Object[]{objValue});
                }catch (Exception e) {
                    e.printStackTrace();
                }
            }

        }catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * @param args
     * @throws Exception 
     */
    public static void main(String[] args) throws Exception {

        //構造測試例子start
        SortUtil<TspPefScoreCalTotal> testSort = new SortUtil<TspPefScoreCalTotal>();
        TspPefScoreCalTotal t1 = new TspPefScoreCalTotal();
        TspPefScoreCalTotal t2 = new TspPefScoreCalTotal();
        TspPefScoreCalTotal t3 = new TspPefScoreCalTotal();
        TspPefScoreCalTotal t4 = new TspPefScoreCalTotal();
        t1.setPefTotalScoreId(1L);
        t1.setBuyerCompanyId(1L);
        t1.setTotalScore(10L);
        t2.setPefTotalScoreId(2L);
        t2.setBuyerCompanyId(1L);
        t2.setTotalScore(5L);
        t3.setPefTotalScoreId(3L);
        t3.setBuyerCompanyId(1L);
        t3.setTotalScore(20L);
        t4.setPefTotalScoreId(4L);
        t4.setBuyerCompanyId(1L);
        t4.setTotalScore(20L);

        List<TspPefScoreCalTotal> list = new ArrayList<TspPefScoreCalTotal>();
        list.add(t1);
        list.add(t2);
        list.add(t3);
        list.add(t4);
        //構造測試例子end

        //對任意物件集合進行指定欄位排序
        testSort.sortGeneral(list, "totalScore", TspPefScoreCalTotal.class, DESC);

        //對於上述已排序的集合,排列名次
        testSort.sortRank(list, TspPefScoreCalTotal.class, "totalScore", "totalScoreRanking", false);

        //輸出結果
        for(Object t:list){
        System.out.println(((TspPefScoreCalTotal)t).getPefTotalScoreId() + "," + ((TspPefScoreCalTotal)t).getTotalScore()
                            + "," + ((TspPefScoreCalTotal)t).getTotalScoreRanking()
                );
        }
    }
}
public class TspPefScoreCalTotal {

private Long pefTotalScoreId; //主鍵ID
private Long totalScore; //得分
private Long totalScoreRanking; //排列名次

public Long getPefTotalScoreId() {
        return this.pefTotalScoreId;
    }
public void setPefTotalScoreId (Long pefTotalScoreId) {
        this.pefTotalScoreId = pefTotalScoreId;
    }

public Long getTotalScore() {
        return this.totalScore;
    }
public void setTotalScore (Long totalScore) {
        this.totalScore = totalScore;
    }
public Long getTotalScoreRanking() {
        return this.totalScoreRanking;
    }
public void setTotalScoreRanking (Long totalScoreRanking) {
        this.totalScoreRanking = totalScoreRanking;
    }   

}

當然,上面的程式還沒有考慮對於大資料量時候的排序處理是否有效能問題。下回找個時間測試一下。