55

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枝を削除しました

4

7 に答える 7

52

Guavaはよりエレガントです。

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

Apache commons-langにも同様の構造のCompareToBuilder.

于 2011-11-07T12:29:01.947 に答える
21
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();
  }
});
于 2011-11-07T12:30:46.780 に答える
20

Java 8 ソリューション:

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

ラムダ、メソッド参照、およびデフォルト メソッドに万歳 :)! 残念なことに、次のように getterを定義するか、明示的な lambdasを使用する必要があります。

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

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

于 2014-07-16T20:49:15.097 に答える
14

まず、ソリューションはそれほど遅くありません。

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

特性ごとに最大 10 ビットを割り当てることができるため、特性ごとに範囲内にある必要があります0..1023

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

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

于 2011-11-07T12:30:34.817 に答える
6

Apache Commons Langの CompareToBuilder を使用できます。(比較可能と説明していますが、Comparator でも機能します)。

于 2011-11-07T12:26:24.857 に答える
4

BeanComparatorsCommons BeanUtils から連鎖して使用できます。

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

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

于 2011-11-07T12:28:10.623 に答える
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);
}
于 2011-11-07T12:29:14.343 に答える