たまーに 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)