Come ordinare l'elenco di oggetti in base a una proprietà


145

Ho una lezione semplice

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

e List<ActiveAlarm>con. Come ordinare in ordine crescente per timeStarted, quindi per timeEnded? Qualcuno può aiutare? Conosco in C ++ l'algoritmo generico e l'operatore di sovraccarico <, ma sono nuovo a Java.


La risposta di @Yishai in questo post dimostra un uso elegante dell'enum per l'ordinamento personalizzato e l'ordinamento raggruppato (argomenti multipli) utilizzando il concatenamento del comparatore.
gunalmel,

Risposte:


141

O fare ActiveAlarmimplementare Comparable<ActiveAlarm>o implementare Comparator<ActiveAlarm>in una classe separata. Quindi chiama:

Collections.sort(list);

o

Collections.sort(list, comparator);

In generale, è una buona idea implementare Comparable<T>se esiste un singolo ordinamento "naturale" ... altrimenti (se ti capita di voler ordinare in un ordine particolare, ma potresti volerne facilmente uno diverso) è meglio implementarlo Comparator<T>. Questa situazione particolare potrebbe andare in entrambi i modi, a dire il vero ... ma probabilmente rimarrei con l' Comparator<T>opzione più flessibile .

EDIT: implementazione di esempio:

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

1
Quella funzione compare () non è in Long probabilmente perché l'implementazione è ancora più banale: return a - b;
papercrane,

5
@papercrane: No, fallisce per motivi di overflow. Prendere in considerazione a = Long.MIN_VALUE, b = 1.
Jon Skeet,

3
da API 19 (KitKat) Long ora ha un.compare
Martin Marconcini il

123

utilizzando Comparator

Per esempio:

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

A partire da Java 8, puoi semplicemente usare l'espressione lambda per rappresentare l'istanza di Comparator.

Collections.sort(scores, (s1, s2) -> { /* compute and return int */ });

1
Cosa fa compareTo()? Da dove viene? Dove devo definirlo?
Asqiir,

1
@Asqiir getScores()è il getter per il scoresquale è un List<Integer>. Quando getScores().get(0)ottieni un Integeroggetto. Integerha già compareTo(anotherInteger)implementato il metodo, non è necessario definirlo.
walen

che cos'è get (0)?
Ali Khaki,

1
Grazie funziona come un incantesimo (lo uso senza la parte .get (0)). Come potrei invertire l'ordine?

53

JAVA 8 e successive risposte (utilizzando espressioni lambda)

In Java 8, le espressioni Lambda sono state introdotte per renderlo ancora più semplice! Invece di creare un oggetto Comparator () con tutte le sue impalcature, puoi semplificarlo come segue: (Usando il tuo oggetto come esempio)

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

o anche più breve:

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

Quell'affermazione equivale alla seguente:

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

Pensa alle espressioni Lambda che richiedono solo di inserire le parti pertinenti del codice: la firma del metodo e ciò che viene restituito.

Un'altra parte della tua domanda era come confrontare più campi. Per fare ciò con le espressioni Lambda, puoi usare la .thenComparing()funzione per combinare efficacemente due confronti in uno:

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

Il codice sopra ordinerà prima l'elenco per timeStarted, quindi per timeEnded(per quei record che hanno lo stesso timeStarted).

Un'ultima nota: è facile confrontare le primitive 'lunghe' o 'int', puoi semplicemente sottrarre l'una dall'altra. Se stai confrontando oggetti ('Long' o 'String'), ti suggerisco di usare il loro confronto integrato. Esempio:

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

EDIT: Grazie a Lukas Eder per avermi indicato la .thenComparing()funzione.


4
Potresti apprezzare la nuova API Java 8 Comparator.comparing().thenComparing()...
Lukas Eder,

1
Fai attenzione con la differenza di ritorno. In caso di overflow, è possibile ottenere risultati errati.
Krzychu,

2
Questo è l'IMHO il metodo di ordinamento più semplice, sempre assumendo che tu non abbia un ovvio ordine naturale. Inoltre, non è più necessario chiamare Collections, è possibile chiamare direttamente nell'elenco. Ad esempio:myList.sort(Comparator.comparing(Address::getZipCode).thenComparing(Compartor.comparing(Address::getStreetName));
CeePlusPlus

20

È possibile ordinare l'elenco in due modi:

1. Utilizzo del comparatore : quando richiesto per utilizzare la logica di ordinamento in più punti Se si desidera utilizzare la logica di ordinamento in un unico luogo, è possibile scrivere una classe interna anonima come segue, oppure estrarre il comparatore e utilizzarlo in più punti

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

Possiamo avere un controllo nullo per le proprietà, se avremmo potuto usare 'Long' anziché 'long'.

2. Utilizzo di Comparable (ordinamento naturale) : se l'algoritmo di ordinamento si attacca sempre a una proprietà: scrivere una classe che implementa 'Comparable' e sovrascrivere il metodo 'compareTo' come definito di seguito

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

}

chiama il metodo di ordinamento per ordinare in base all'ordinamento naturale

Collections.sort(list);

7

In java8 + questo può essere scritto su una sola riga come segue:

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

codice:

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

o

ListOfActiveAlarmObj.sort(Comparator.comparing(ActiveAlarm::getTimeStarted))

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

Questo dovrebbe darti un'idea approssimativa. Una volta fatto, puoi chiamare Collections.sort()sulla lista.


4

Da Java8 questo può essere fatto in modo ancora più pulito usando una combinazione di ComparatoreLambda expressions

Per esempio:

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

2

Guava's ComparisonChain :

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


1

In Java è necessario utilizzare il Collections.sortmetodo statico . Ecco un esempio per un elenco di oggetti CompanyRole, ordinati prima per inizio e poi per fine. Puoi adattarti facilmente al tuo oggetto.

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

0

Puoi chiamare Collections.sort () e passare un comparatore che devi scrivere per confrontare le diverse proprietà dell'oggetto.


0

Come detto, puoi ordinare per:

  • Realizzare il tuo oggetto Comparable
  • Oppure passa un ComparatoraCollections.sort

Se fai entrambe le cose, Comparableverranno ignorate e Comparatorverranno utilizzate. Questo aiuta a far sì che gli oggetti valore abbiano il proprio Comparableordinamento logico più ragionevole per l'oggetto valore, mentre ogni singolo caso d'uso ha la propria implementazione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.