web-dev-qa-db-ja.com

ソートJava複数のキーを使用したオブジェクト

Duckオブジェクトのコレクションを取得しました複数のキーを使用してそれらを並べ替えますにしたいと思います。

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

例えば。 主に重みで主に年齢でをソートしたい。 2羽のアヒルの体重と年齢がまったく同じである場合は、それらの名前を3次キーとしてを使用して区別します。私はこのようなことをするかもしれません:

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 [〜#〜]不要なelseブランチを削除

53
andras

Java 8ソリューション:

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

ラムダ、メソッド参照、およびデフォルトのメソッドのためのHooray :)!次のように、ゲッターを定義するか、明示的なラムダを使用する必要があります。

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

型推論は暗黙のラムダでは機能しないため、最初の2つのラムダの引数の型を指定する必要があります。詳細は Brian Goetzによるこの回答 を参照してください。

12
andras

Guava の方がエレガントです:

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

Apache commons-lang にも同様の構成 CompareToBuilder があります。

50
JB Nizet
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();
  }
});
20
Boris Pavlović

まず、あなたの解決策はthat遅くはありません。

本当に別の方法が必要な場合は、各アヒルに3つの特性の合計である基本的に単一の数値である「スコア」を指定しますが、重みについては非常に大きな重み(ほとんど避けられない言い訳)があり、年齢については小さいです;そして名前のための非常に小さなもの。

各特性に最大10ビットを割り当てることができるため、各特性については0..1023

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

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

14
Noxville

CompareToBuilderは Apache Commons Lang から使用できます。 (これは比較可能ですが、コンパレータでも機能します)。

6
Thirler

Commons BeanUtilsのチェーンBeanComparatorsを使用できます。

Comparator comparator = new BeanComparator("weight", new BeanComparator("age"));

http://commons.Apache.org/beanutils/v1.8.3/apidocs/org/Apache/commons/beanutils/BeanComparator.html

4
Emmanuel Bourg

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);
}
4
AlexR