57 Stimmen

Sortieren von Java-Objekten mit mehreren Schlüsseln

Ich habe eine Sammlung von Duck-Objekten und würde gerne sie nach mehreren Schlüsseln sortieren .

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

z.B.. Ich möchte sie sortieren in erster Linie durch ihre Gewichte y in zweiter Linie durch ihr Alter . Wenn zwei Enten genau dasselbe Gewicht und genau dasselbe Alter haben, dann unterscheiden wir sie anhand ihrer Namen als Tertiärschlüssel . Ich könnte etwas in dieser Art machen:

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

Ich mache das recht häufig, aber diese Lösung riecht nicht gut. Sie lässt sich nicht gut skalieren, und man kann es leicht vermasseln. Es muss doch einen besseren Weg geben, die Ducks mit mehreren Tasten zu sortieren! Kennt jemand eine bessere Lösung?

EDITAR Unnötiges entfernt else Zweige

52voto

JB Nizet Punkte 654813

Guave ist eleganter:

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

Apache commons-lang hat ein ähnliches Konstrukt, CompareToBuilder .

28voto

andras Punkte 5947

Java 8 Lösung:

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

Ein Hoch auf Lambdas, Methodenreferenzen und Standardmethoden:)! Schade, dass wir Getter definieren müssen, oder explizite Lambdas , etwa so:

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

Die Typinferenz funktioniert nicht mit impliziten Lambdas, daher müssen Sie den Argumenttyp der ersten beiden Lambdas angeben. Mehr Details unter diese Antwort von Brian Goetz .

21voto

Boris Pavlović Punkte 60636
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();
  }
});

14voto

Noxville Punkte 530

Erstens: Ihre Lösung ist nicht dass langsam.

Wenn Sie wirklich eine andere Methode wollen, dann geben Sie jeder Ente eine "Punktzahl", die im Wesentlichen eine einzige Zahl ist, die die Summe ihrer drei Merkmale darstellt, aber mit einer großen Gewichtung (entschuldigen Sie das fast unvermeidliche Wortspiel) für das Gewicht, einer geringeren für das Alter und einer sehr kleinen für den Namen.

Für jedes Merkmal können Sie ~10 Bits zuweisen, so dass Sie für jedes Merkmal im Bereich von 0..1023 .

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

Das ist wahrscheinlich völlig überflüssig, aber egal :)

6voto

Thirler Punkte 19460

Sie können den CompareToBuilder von Apache Commons Lang . (Es erklärt vergleichbar, aber funktioniert auch für Comparator).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X