たまーに Java でクラスのソートをしたくなるので、ユーティリティクラスを作ってみました。
private および protected フィールドの値を取得するために、リフレクション時に Field#setAccessible() を利用していますが、
これを使用したくない場合は sortutil.SortUtil の SortComparator#getFieldValue() の該当の箇所を削除してください。
なお、Field#setAccessible() を使用しない場合には、private フィールド、および、別パッケージの protected フィールドはソートの対象にできなくなります。
package sortutil; import java.lang.reflect.*; import java.util.*; /** * クラスのソートのためのユーティリティ */ public class SortUtil<T> { /** * ソートオーダー */ public enum SortOrder { ASCENDING, // 昇順 DESCENDING, // 降順 } // ソートのための比較クラス private SortComparator sortComparator = new SortComparator(); /** * ソートの情報を追加する * @param itemName 項目名 * @param order ソートオーダー */ public void add(String itemName, SortOrder order) { sortComparator.add(new SortInfo(itemName, order)); } /** * ソートを行う * @param list ソート対象の List オブジェクト */ public void sort(List<T> list) { Collections.sort(list, sortComparator); } // ソートの情報 private class SortInfo { // 項目名 private String itemName; public String getItemName() { return itemName; } // ソートオーダー private SortOrder order; public SortOrder getOrder() { return order; } // コンストラクタ public SortInfo(String itemName, SortOrder order) { this.itemName = itemName; this.order = order; } } // ソートのための比較クラス private class SortComparator implements Comparator<T> { // ソートの情報を保持する private List<SortInfo> sortInfoList = new ArrayList<SortInfo>(); // ソートの情報を追加する public void add(SortInfo sortInfo) { sortInfoList.add(sortInfo); } // 比較を行う(Comparator#compare() の実装) @Override public int compare(T item1, T item2) { try { int result = 0; // ソートの優先順位の高い項目から順に比較する。 for (int i = 0; i < sortInfoList.size(); ++i) { SortInfo sortInfo = sortInfoList.get(i); Object value1 = getFieldValue(item1, sortInfo.getItemName()); Object value2 = getFieldValue(item2, sortInfo.getItemName()); result = compareInternal(value1, value2); if (result == 0) { // 項目値が同じであれば、次に優先順位の高い項目の比較を行う continue; } if (sortInfo.getOrder() == SortOrder.DESCENDING) { result *= -1; } break; } return result; } catch (Exception ex) { throw new RuntimeException(ex.toString()); } } // 実際の比較処理を行う private int compareInternal(Object value1, Object value2) { if (value1 == null && value2 == null) { return 0; } else if (value1 == null && value2 != null) { return -1; } else if (value1 != null && value2 == null) { return 1; } else { // TODO: とりあえず以下の型のみ。必要に応じ適宜追加してください。 if (value1 instanceof Integer) { return ((Integer) value1).compareTo((Integer) value2); } else if (value1 instanceof Long) { return ((Long) value1).compareTo((Long) value2); } else if (value1 instanceof String) { return ((String) value1).compareTo((String) value2); } else { throw new RuntimeException("対応していない型です: " + value1.getClass().getCanonicalName()); } } } // フィールドの値を得る private Object getFieldValue(Object obj, String fieldName) { try { Field field = obj.getClass().getDeclaredField(fieldName); // NOTE: Field#setAccessible() を使用したくない場合は、この行を削除してください。 // なお、削除した場合には、private フィールド、および、別パッケージの protected フィールドは // ソートの対象にできません。 field.setAccessible(true); return field.get(obj); } catch (Exception ex) { throw new RuntimeException("フィールド値を取得できません: " + ex.toString()); } } } }
以下のような感じで使います。
import java.util.*; import sortutil.SortUtil; public class Main { // ソート対象のクラス public static class TestItem { public String NaMe1; private int value1; public String name2; public int getValue1() { return value1; } public TestItem(String name1, int value1, String name2) { this.NaMe1 = name1; this.value1 = value1; this.name2 = name2; } } // ソートのユーティリティの使用例 public static void main(String[] args) { try { // ソート対象のデータを List に詰め込む List<TestItem> list = new ArrayList<TestItem>(); list.add(new TestItem("c", 2, "BCD")); list.add(new TestItem("a", 1, "EFG")); list.add(new TestItem("b", 1, "CDE")); list.add(new TestItem("c", 1, "ABC")); list.add(new TestItem("a", 2, "FGH")); list.add(new TestItem("b", 2, "DEF")); // ソートのユーティリティの準備をする SortUtil<TestItem> su = new SortUtil<TestItem>(); // ・第 1 引数(項目名): ソート対象のクラスのフィールド名を指定(大文字・小文字を区別する)する // ・第 2 引数(ソートオーダー): 昇順・降順を指定する su.add("NaMe1", SortUtil.SortOrder.DESCENDING); su.add("value1", SortUtil.SortOrder.ASCENDING); // ソート対象のデータの List を渡しソートする su.sort(list); printTestItemList(list); } catch (Exception ex) { ex.printStackTrace(); } } // ソート結果を出力する public static void printTestItemList(List<TestItem> list) { System.out.println(">>----------"); for (TestItem ti : list) { System.out.println(ti.NaMe1 + "," + ti.getValue1() + "," + ti.name2); } System.out.println("<<----------"); } }
実行結果
>>---------- c,1,ABC c,2,BCD b,1,CDE b,2,DEF a,1,EFG a,2,FGH <<----------
java ソースのエンコーディングが UTF-8 になっていますので、お気を付け下さい。
なお、プログラムの作成には
Eclipse 3.6 Helios Pleiades All in One (MergeDoc Project で公開されているバージョンです) を利用しました。
大変有益なリソースを公開されており、いつも利用させていただいております。ありがたく深謝いたします。
SortUtil.zip (20.1 KB)