複数の並べ替え可能な要素を持つオブジェクトのリストをどのように並べ替えますか?
単純なオブジェクトCar
があり、carがそのように定義されているとします。
class Car {
public String make;
public String model;
public int year;
public String color;
// ... No methods, just a structure / container
}
複数のSortOption
sをSorter
に提供して、リストをソートできるようにする単純なフレームワークを設計しました。
interface ISorter<T> {
List<T> sort(List<T> items);
void addSortOption(ISortOption<T> option);
ISortOption<T>[] getSortOptions();
void setSortOption(ISortOption<T> option);
}
interface ISortOption<T> {
String getLabel();
int compare(T t1, T t2);
}
使用例
class SimpleStringSorter extends MergeSorter<String> {
{
addSorter(new AlphaSorter());
}
private static final class AlphaSorter implements ISortOption<String> {
// ... implementation of alpha compare and get label
}
}
このソリューションの問題は、簡単に拡張できないことです。車が新しいフィールド、たとえばcurrentOwnerを受け取る場合。フィールドを追加してから、ソータークラスファイルを追跡し、新しい並べ替えオプションクラスを実装してから、アプリケーションを再コンパイルして再配布する必要があります。
このようにデータをソートするより簡単で拡張可能な/実用的な方法はありますか?
実際には、compare(a、b)メソッドを実装したコンパレータを使用できます。
次に、それを比較ステップに渡すことができます(これは、ほとんどの言語のほぼすべての標準ライブラリでサポートされています)。
たとえば、Javaでは、
Collections.sort(fooList, new Comparator<Car>(){
public int compare(Car a,Car b){
return a.getModel().compareTo(b.getModel());
// or compare what you want return -1, 0 or 1
// for less than, equal and greater than resp.
}
});
カスタムコンパレーターに従ってリストを並べ替えるには
Java 8には、コンパレータを1行で作成するためのラムダ構文があります。
つまり、維持するソートアルゴリズムは1つだけであり、比較対象と同じクラス(または比較が行われている場所の近く)にとどまることができる多くのコンパレータがあります。
これにより、「階層化」ソートが可能になり、次のようなものを実装できます。
public static Comparator<T> createTieredComparator(final Comparator<? super T> comp1, final Comparator<? super T> comp2){
return new Comparator<T>(){
public int compare(T a,T b){
int res = comp1.compare(a,b);
if(res!=0)
return res;
else
return comp2.compare(a,b);
}
};
}
これは、comp1
による比較を優先し、comp2
に従って等しいと見なされる場合にのみcomp1
の結果を返します。
Java.lang.Comparableを実装し、「自然な順序付け」に依存するJava.util.TreeSetのようなコレクションを使用します。 ComparableインターフェースでのcompareTo()メソッドの実装は、 equals()メソッドの実装 によく似ています。このリンクには、両方の例が含まれています。 compareTo()を実装し、3つすべてが相互に互換性がある必要がある場合は、equals()とhashcode()を実装することが重要です!
private int compareNullHelper(Object a, Object b) {
// sorts nulls first
if (a == null) {
if (b != null) { return -1; }
} else if (b == null) { return 1; }
return a.compareTo(b);
}
/** Sorts by make, model, year, color. */
@Override
public int compareTo(Car that) {
// ensures consistency with equals()
if (this.equals(that)) { return 0; }
int ret = compareNullHelper(this.make, that.make);
if (ret != 0) { return ret; }
ret = compareNullHelper(this.model, that.model);
if (ret != 0) { return ret; }
ret = this.year - that.year;
if (ret != 0) { return ret; }
ret = compareNullHelper(this.color, that.color);
if (ret != 0) { return ret; }
// If you can't tell, return -1 which means that they are in the right
// order but unequal - ensures consistency with equals()
return -1;
}
今、あなたは書くことができます:
Set<Car> orderedCars = new TreeSet<Car>();
orderedCars.addAll(myCars);
異なる基準に従って並べ替える必要がある場合は、並べ替え順序ごとに追加のコンパレータクラス(別のJava APIインターフェース)を作成します。CarにComparableとCarのcompareTo()メソッドを実装させる場合クラスは、メーカー、モデル、年、色で決まります。ByColorComparator implements Comparator
色、年、メーカー、モデルで比較します。次に言う:
Set<Car> carsByColor = new TreeSet<Car>(new ByColorComparator());
orderedCars.addAll(myCars);
Javaコレクションの組み込みソートはめちゃくちゃ速いです。何年もの間Javaコレクションは、プラットフォームの最高のものの1つでした。
注意すべき点の1つは、この手法はHibernateまたはJPA(開いているデータベースセッションがある場合)と連携して、ソートに必要な可能性のあるデータベースから関連オブジェクトを自動的に取得することです。