2011-11-07 2 views
47

私はDuckオブジェクトのコレクションを持っています。複数のキーを使って並べ替えたいと思います。複数のキーを使用してJavaオブジェクトを並べ替える

class Duck { 
    DuckAge age; //implements Comparable 
    DuckWeight weight; //implements Comparable 
    String name; 
} 
List<Duck> ducks = Pond.getDucks(); 

例: は主にウエイトがが副次的にでソートしたいと思っています。もし2頭のアヒルの体重が全く同じで年齢が同じであれば、という名前のを使って区別しましょう。私はこのような何かをするかもしれません:

Collections.sort(ducks, new Comparator<Duck>(){ 
    @Override 
    public int compare(Duck d1, Duck d2){ 
     int weightCmp = d1.weight.compareTo(d2.weight); 
     if (weightCmp != 0) { 
      return weightCmp; 
     } 
     int ageCmp = d1.age.compareTo(d2.age); 
     if (ageCmp != 0) { 
      return ageCmp; 
     } 
     return d1.name.compareTo(d2.name); 
    } 
}); 

私はこれを非常に頻繁に行いますが、この解決策は正しく臭いません。それはうまくスケールされていませんし、それを台無しにするのは簡単です。確かに、複数のキーを使ってアヒルを選ぶ良い方法があるはずです!誰かがより良い解決策を知っていますか?

EDITはあなたがApache Commons LangからCompareToBuilderを使用することができ、不要なelse

+6

それはあまりにも悪くはありません。インデントのレベルを削除するには、 'else'の両方を削除します。 – stivlo

+14

+1あなたのカモを注文するには – Rich

+0

コアJava自体に優雅なソリューションはありませんか? – Sid

答えて

9

のJava 8ソリューション:

Comparator<Duck> cmp = Comparator.comparing(Duck::getWeight) 
    .thenComparing(Duck::getAge) 
    .thenComparing(Duck::getName); 

万歳ラムダ、メソッド参照、およびデフォルトの方法について:)!

Comparator<Duck> cmp = Comparator 
    .comparing((Duck duck)-> duck.weight) 
    .thenComparing((Duck duck)-> duck.age) 
    .thenComparing(duck-> duck.name); 

型推論は、暗黙のラムダでは動作しませんので、あなたは、最初の2つのラムダの引数の型を指定する必要があります。あまりにも悪い我々はそうのように、ゲッターを定義する、または明示的なラムダ使用する必要があります。詳細はthis answer by Brian Goetzを参照してください。

6

を削除しました。 (それは匹敵すると説明しますが、Comparatorでも使えます)。

48

Guavaは、よりエレガントである:

return ComparisonChain.start() 
    .compare(d1.weight, d2.weight) 
    .compare(d1.age, d2.age) 
    .compare(d1.name, d2.name) 
    .result(); 

アパッチcommons-langCompareToBuilder、同様の構造を持っています。

+1

ありがとう、これは私が欲しかったものです! [Guava Ordering](http://google-collections.googlecode.com/svn/trunk/javadoc/index.html?com/google/common/collect/Ordering.html)クラスも素敵ですコンパレータを組み合わせてソートに使用したい – andras

+3

ApacheのCompareToBuilderは、nullsの最初の比較を使用して、デフォルトでnullを処理するため、少しエレガントです。 GuavaのComparisonChainは、すべての.compare()呼び出しに3つ目のパラメータ(Ordering.natural()。nullsFirst())を追加しない限り、NullPointerExceptionをスローします。 –

+2

もしあなたがヌルを好きなら、 –

4

ネストされたelse文なしでコードを書き直しました。今は好きですか?

@Override 
public int compare(Duck d1, Duck d2){ 
    int weightCmp = d1.weight.compareTo(d2.weight); 
    if (weightCmp != 0) { 
     return weightCmp; 
    } 
    int ageCmp = d1.age.compareTo(d2.age); 
    if (ageCmp != 0) { 
     return ageCmp; 
    } 

    return d1.name.compareTo(d2.age); 
} 
+0

ええ、ありがとう、それは良く見えますが、主な問題は私が手動で比較を連鎖していたことでした。 Guava ComparisonChainとApache CompareToBuilderの方がはるかに優れています。 – andras

14

まず、あなたの解決策はないという遅いです。

あなたが本当に別の方法を望むなら、各ダックに本質的に3つの特性の合計である単一の数字であるが、重量については巨大な重み付け(ほとんど避けることのできない言い訳)年齢の低い方。名前のための非常に小さいもの。

各特性に対して〜10ビットを割り当てることができるので、各特性については、0..1023の範囲内になければなりません。

score = ((weight << 10) + age) << 10 + name; 

これはおそらく完全に不要ですが、何でも:)

+0

素敵な小さなトリック、ありがとう。パフォーマンスではなく美しさを追求していましたが、これを覚えておいてください:) – andras

20
List<Duck> ducks = new ArrayList<Duck>(); 
Collections.sort(ducks, new Comparator<Duck>() { 

    @Override 
    public int compare(Duck o1, Duck o2) { 

    return new org.apache.commons.lang.builder.CompareToBuilder(). 
     append(o1.weight, o2.weight). 
     append(o1.age, o2.age). 
     append(o1.name, o2.name). 
     toComparison(); 
    } 
}); 
+0

Arraylistsをソートするにはどうすればいいですか? –

+0

配列(例:String [])やjava.util.ArrayListsについて話していますか?それは格納されている要素によって異なります。配列は、 'Arrays.sort(T []、Comparator )' –

+0

というメソッドを使ってソートすることができます。この例では、アヒル型の2つのオブジェクトをソートしました.... "public int compare o1、Duck o2) "もし、私がDuck型の配列全体をソートしなければならない場合はどうでしょうか?例えばArrayList dd = new ArrayList(); ????? –

関連する問題