So sortieren Sie die Liste der Objekte nach einer Eigenschaft

144

Ich habe einfache Klasse

public class ActiveAlarm {
    public long timeStarted;
    public long timeEnded;
    private String name = "";
    private String description = "";
    private String event;
    private boolean live = false;
}

und List<ActiveAlarm>con. Wie sortiere ich in aufsteigender Reihenfolge nach timeStarted, dann nach timeEnded? Kann jemand helfen? Ich kenne mich in C ++ mit generischem Algorithmus und Überladungsoperator <aus, bin aber neu in Java.

Jennifer
quelle
Die Antwort von @Yishai in diesem Beitrag zeigt die elegante Verwendung von Enum für die benutzerdefinierte Sortierung und Gruppensortierung (mehrere Argumente) unter Verwendung der Komparatorverkettung.
Gunalmel

Antworten:

140

Entweder ActiveAlarmimplementieren Comparable<ActiveAlarm>oder Comparator<ActiveAlarm>in einer separaten Klasse implementieren . Dann ruf an:

Collections.sort(list);

oder

Collections.sort(list, comparator);

Im Allgemeinen ist es eine gute Idee, sie zu implementieren, Comparable<T>wenn es eine einzige "natürliche" Sortierreihenfolge gibt. Andernfalls (wenn Sie zufällig in einer bestimmten Reihenfolge sortieren möchten, aber genauso leicht eine andere möchten) ist es besser, sie zu implementieren Comparator<T>. Diese besondere Situation könnte in beide Richtungen gehen, um ehrlich zu sein ... aber ich würde wahrscheinlich bei der flexibleren Comparator<T>Option bleiben .

EDIT: Beispielimplementierung:

public class AlarmByTimesComparer implements Comparator<ActiveAlarm> {
  @Override
  public int compare(ActiveAlarm x, ActiveAlarm y) {
    // TODO: Handle null x or y values
    int startComparison = compare(x.timeStarted, y.timeStarted);
    return startComparison != 0 ? startComparison
                                : compare(x.timeEnded, y.timeEnded);
  }

  // I don't know why this isn't in Long...
  private static int compare(long a, long b) {
    return a < b ? -1
         : a > b ? 1
         : 0;
  }
}
Jon Skeet
quelle
1
Diese compare () -Funktion ist nicht wahrscheinlich, da die Implementierung noch trivialer ist: return a - b;
Papierkran
5
@papercrane: Nein, das schlägt aus Überlaufgründen fehl. Überlegen Sie a = Long.MIN_VALUE, b = 1.
Jon Skeet
3
ab API 19 (KitKat) Long hat jetzt einen.compare
Martin Marconcini
123

Verwenden von Comparator

Beispielsweise:

class Score {

    private String name;
    private List<Integer> scores;
    // +accessor methods
}

    Collections.sort(scores, new Comparator<Score>() {

        public int compare(Score o1, Score o2) {
            // compare two instance of `Score` and return `int` as result.
            return o2.getScores().get(0).compareTo(o1.getScores().get(0));
        }
    });

Ab Java 8 können Sie einfach den Lambda-Ausdruck verwenden, um die Comparator-Instanz darzustellen.

Collections.sort(scores, (s1, s2) -> { /* compute and return int */ });
Jigar Joshi
quelle
1
Was macht compareTo()das Woher kommt das? Wo muss ich es definieren?
Asqiir
1
@Asqiir getScores()ist der Getter, für scoresden a List<Integer>. Wenn Sie dies tun, erhalten getScores().get(0)Sie ein IntegerObjekt. IntegerIst die compareTo(anotherInteger)Methode bereits implementiert, müssen Sie sie nicht definieren.
Wal
Was ist get (0)?
Ali Khaki
1
Danke funktioniert wie ein Zauber (ich benutze es ohne den .get (0) Teil). Wie könnte ich die Reihenfolge umkehren?
51

JAVA 8 und höher Antwort (mit Lambda-Ausdrücken)

In Java 8 wurden Lambda-Ausdrücke eingeführt, um dies noch einfacher zu machen! Anstatt ein Comparator () -Objekt mit seinem gesamten Gerüst zu erstellen, können Sie es wie folgt vereinfachen: (Verwenden Sie Ihr Objekt als Beispiel)

Collections.sort(list, (ActiveAlarm a1, ActiveAlarm a2) -> a1.timeStarted-a2.timeStarted);

oder noch kürzer:

Collections.sort(list, Comparator.comparingInt(ActiveAlarm ::getterMethod));

Diese eine Aussage entspricht der folgenden:

Collections.sort(list, new Comparator<ActiveAlarm>() {
    @Override
    public int compare(ActiveAlarm a1, ActiveAlarm a2) {
        return a1.timeStarted - a2.timeStarted;
    }
});

Stellen Sie sich Lambda-Ausdrücke so vor, dass Sie nur die relevanten Teile des Codes eingeben müssen: die Methodensignatur und das, was zurückgegeben wird.

Ein weiterer Teil Ihrer Frage war, wie Sie mit mehreren Feldern vergleichen können. Um dies mit Lambda-Ausdrücken zu tun, können Sie die .thenComparing()Funktion verwenden, um zwei Vergleiche effektiv zu einem zu kombinieren:

Collections.sort(list, (ActiveAlarm a1, ActiveAlarm a2) -> a1.timeStarted-a2.timeStarted             
       .thenComparing ((ActiveAlarm a1, ActiveAlarm a2) -> a1.timeEnded-a2.timeEnded)
);

Der obige Code sortiert die Liste zuerst nach timeStartedund dann nach timeEnded(für die Datensätze, die denselben haben timeStarted).

Ein letzter Hinweis: Es ist einfach, "lange" oder "int" Grundelemente zu vergleichen. Sie können einfach eines vom anderen subtrahieren. Wenn Sie Objekte vergleichen ('Long' oder 'String'), empfehle ich Ihnen, deren integrierten Vergleich zu verwenden. Beispiel:

Collections.sort(list, (ActiveAlarm a1, ActiveAlarm a2) -> a1.name.compareTo(a2.name) );

EDIT: Danke an Lukas Eder, der mich auf die .thenComparing()Funktion hingewiesen hat .

John Fowler
quelle
4
Sie werden vielleicht die neue Java 8 API zu schätzen wissen Comparator.comparing().thenComparing()...
Lukas Eder
1
Achten Sie auf den Unterschied. Im Falle eines Überlaufs können Sie ein falsches Ergebnis erhalten.
Krzychu
2
Dies ist meiner Meinung nach die einfachere Sortiermethode, vorausgesetzt, Sie haben keine offensichtliche natürliche Reihenfolge. Sie müssen auch nicht Collectionsmehr anrufen , Sie können direkt auf die Liste anrufen. Zum Beispiel:myList.sort(Comparator.comparing(Address::getZipCode).thenComparing(Compartor.comparing(Address::getStreetName));
CeePlusPlus
20

Wir können die Liste auf zwei Arten sortieren:

1. Verwenden des Komparators : Wenn erforderlich, um die Sortierlogik an mehreren Stellen zu verwenden Wenn Sie die Sortierlogik an einer einzigen Stelle verwenden möchten, können Sie eine anonyme innere Klasse wie folgt schreiben oder den Komparator extrahieren und an mehreren Stellen verwenden

  Collections.sort(arrayList, new Comparator<ActiveAlarm>() {
        public int compare(ActiveAlarm o1, ActiveAlarm o2) {
            //Sorts by 'TimeStarted' property
            return o1.getTimeStarted()<o2.getTimeStarted()?-1:o1.getTimeStarted()>o2.getTimeStarted()?1:doSecodaryOrderSort(o1,o2);
        }

        //If 'TimeStarted' property is equal sorts by 'TimeEnded' property
        public int doSecodaryOrderSort(ActiveAlarm o1,ActiveAlarm o2) {
            return o1.getTimeEnded()<o2.getTimeEnded()?-1:o1.getTimeEnded()>o2.getTimeEnded()?1:0;
        }
    });

Wir können die Eigenschaften auf Null prüfen lassen, wenn wir 'Long' anstelle von 'long' hätten verwenden können.

2. Verwenden von Comparable (natürliche Reihenfolge) : Wenn der Sortieralgorithmus immer an einer Eigenschaft festhält: Schreiben Sie eine Klasse, die 'Comparable' implementiert, und überschreiben Sie die 'compareTo'-Methode wie unten definiert

class ActiveAlarm implements Comparable<ActiveAlarm>{

public long timeStarted;
public long timeEnded;
private String name = "";
private String description = "";
private String event;
private boolean live = false;

public ActiveAlarm(long timeStarted,long timeEnded) {
    this.timeStarted=timeStarted;
    this.timeEnded=timeEnded;
}

public long getTimeStarted() {
    return timeStarted;
}

public long getTimeEnded() {
    return timeEnded;
}

public int compareTo(ActiveAlarm o) {
    return timeStarted<o.getTimeStarted()?-1:timeStarted>o.getTimeStarted()?1:doSecodaryOrderSort(o);
}

public int doSecodaryOrderSort(ActiveAlarm o) {
    return timeEnded<o.getTimeEnded()?-1:timeEnded>o.getTimeEnded()?1:0;
}

}}

Rufen Sie die Sortiermethode auf, um nach natürlicher Reihenfolge zu sortieren

Collections.sort(list);
Jagadeesh
quelle
7

In Java8 + kann dies wie folgt in einer Zeile geschrieben werden:

collectionObjec.sort(comparator_lamda) oder comparator.comparing(CollectionType::getterOfProperty)

Code:

ListOfActiveAlarmObj.sort((a,b->a.getTimeStarted().compareTo(b.getTimeStarted())))
 

oder

ListOfActiveAlarmObj.sort(Comparator.comparing(ActiveAlarm::getTimeStarted))
Karthikeyan
quelle
5
public class ActiveAlarm implements Comparable<ActiveAlarm> {
    public long timeStarted;
    public long timeEnded;
    private String name = "";
    private String description = "";
    private String event;
    private boolean live = false;

    public int compareTo(ActiveAlarm a) {
        if ( this.timeStarted > a.timeStarted )
            return 1;
        else if ( this.timeStarted < a.timeStarted )
            return -1;
        else {
             if ( this.timeEnded > a.timeEnded )
                 return 1;
             else
                 return -1;
        }
 }

Das sollte Ihnen eine ungefähre Vorstellung geben. Sobald dies erledigt ist, können Sie Collections.sort()die Liste aufrufen .

Kal
quelle
4

Seit Java8 kann dies mit einer Kombination aus Comparatorund noch sauberer gemacht werdenLambda expressions

Beispielsweise:

class Student{

    private String name;
    private List<Score> scores;

    // +accessor methods
}

class Score {

    private int grade;
    // +accessor methods
}

    Collections.sort(student.getScores(), Comparator.comparing(Score::getGrade);
Mathias G.
quelle
2

Guavas Vergleichskette :

Collections.sort(list, new Comparator<ActiveAlarm>(){
            @Override
            public int compare(ActiveAlarm a1, ActiveAlarm a2) {
                 return ComparisonChain.start()
                       .compare(a1.timestarted, a2.timestarted)
                       //...
                       .compare(a1.timeEnded, a1.timeEnded).result();
            }});
Gy 声 远 Shengyuan Lu
quelle
1

In Java müssen Sie die statische Collections.sortMethode verwenden. Hier ist ein Beispiel für eine Liste von CompanyRole-Objekten, die zuerst nach Anfang und dann nach Ende sortiert sind. Sie können sich leicht an Ihr eigenes Objekt anpassen.

private static void order(List<TextComponent> roles) {

    Collections.sort(roles, new Comparator() {
        @Override
        public int compare(Object o1, Object o2) {
            int x1 = ((CompanyRole) o1).getBegin();
            int x2 = ((CompanyRole) o2).getBegin();

            if (x1 != x2) {
                return x1 - x2;
            } else {
                int y1 = ((CompanyRole) o1).getEnd();
                int y2 = ((CompanyRole) o2).getEnd();
                return y2 - y1;
            }
        }
    });
}
Richard H.
quelle
0

Sie können Collections.sort () aufrufen und einen Komparator übergeben, den Sie schreiben müssen, um verschiedene Eigenschaften des Objekts zu vergleichen.

Liv
quelle
0

Wie bereits erwähnt, können Sie sortieren nach:

  • Lassen Sie Ihr Objekt implementieren Comparable
  • Oder geben Sie ein ComparatoranCollections.sort

Wenn Sie beides tun, Comparablewird das ignoriert und Comparatorverwendet. Dies hilft, dass die Wertobjekte eine eigene Logik haben, Comparabledie für Ihr Wertobjekt am sinnvollsten ist, während jeder einzelne Anwendungsfall seine eigene Implementierung hat.

Alireza Fattahi
quelle