Cómo ordenar la lista de objetos por alguna propiedad

145

Tengo clase simple

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

y List<ActiveAlarm>con. ¿Cómo ordenar en orden ascendente por timeStarted, luego por timeEnded? ¿Alguien puede ayudar? Sé en C ++ con algoritmo genérico y operador de sobrecarga <, pero soy nuevo en Java.

Jennifer
fuente
La respuesta de @Yishai en esta publicación demuestra el uso elegante de enum para la clasificación personalizada y la clasificación agrupada (argumentos múltiples) utilizando el encadenamiento de comparación.
gunalmel

Respuestas:

141

O bien ActiveAlarmimplemente Comparable<ActiveAlarm>o implemente Comparator<ActiveAlarm>en una clase separada. Luego llame:

Collections.sort(list);

o

Collections.sort(list, comparator);

En general, es una buena idea para poner en práctica Comparable<T>si hay una sola orden "natural" ... en caso contrario (si por casualidad que desee ordenar en un orden particular, pero igualmente podría querer fácilmente una diferente) que es mejor para poner en práctica Comparator<T>. Esta situación particular podría ir en cualquier dirección, para ser honesto ... pero probablemente me quedaría con la Comparator<T>opción más flexible .

EDITAR: Implementación de muestra:

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
fuente
1
Esa función compare () no está en Long probablemente porque la implementación es aún más trivial: return a - b;
papercrane
55
@ papercrane: No, eso falla por razones de desbordamiento. Considere a = Long.MIN_VALUE, b = 1.
Jon Skeet
3
a partir de API 19 (KitKat) Long ahora tiene un.compare
Martin Marconcini
123

Utilizando Comparator

Por ejemplo:

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

Con Java 8 en adelante, simplemente puede usar la expresión lambda para representar la instancia de Comparator.

Collections.sort(scores, (s1, s2) -> { /* compute and return int */ });
Jigar Joshi
fuente
1
¿Qué compareTo()hacer? ¿De dónde viene? ¿Dónde tengo que definirlo?
Asqiir
1
@Asqiir getScores()es el getter para el scorescual es un List<Integer>. Cuando lo haces getScores().get(0), obtienes un Integerobjeto. Integerya tiene compareTo(anotherInteger)implementado el método, no tiene que definirlo.
walen
¿Qué es get (0)?
Ali Khaki
1
Gracias funciona de maravilla (lo uso sin la parte .get (0)). ¿Cómo podría revertir el orden?
53

JAVA 8 y la respuesta anterior (Uso de expresiones Lambda)

¡En Java 8, se introdujeron expresiones Lambda para hacer esto aún más fácil! En lugar de crear un objeto Comparator () con todos sus andamios, puede simplificarlo de la siguiente manera: (Usando su objeto como ejemplo)

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

o incluso más corto:

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

Esa declaración es equivalente a lo siguiente:

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

Piense en las expresiones de Lambda como algo que solo requiere que ingrese las partes relevantes del código: la firma del método y lo que se devuelve.

Otra parte de su pregunta fue cómo comparar con múltiples campos. Para hacer eso con las expresiones Lambda, puede usar la .thenComparing()función para combinar efectivamente dos comparaciones en una:

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

El código anterior ordenará la lista primero por timeStarted, y luego por timeEnded(para aquellos registros que tengan el mismo timeStarted).

Una última nota: es fácil comparar primitivas 'largas' o 'int', solo puede restar una de la otra. Si está comparando objetos ('Long' o 'String'), le sugiero que use su comparación integrada. Ejemplo:

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

EDITAR: Gracias a Lukas Eder por indicarme que .thenComparing()funcione.

John Fowler
fuente
44
Puede que aprecie la nueva API de Java 8 Comparator.comparing().thenComparing()...
Lukas Eder
1
Cuidado con la diferencia que regresa. En caso de desbordamiento, puede obtener un resultado incorrecto.
krzychu
2
Este es, en mi humilde opinión, el método de clasificación más fácil, suponiendo nuevamente que no tiene un orden natural obvio. Tampoco necesita llamar Collectionsmás, puede llamar directamente a la lista. Por ejemplo:myList.sort(Comparator.comparing(Address::getZipCode).thenComparing(Compartor.comparing(Address::getStreetName));
CeePlusPlus
20

Podemos ordenar la lista de una de dos maneras:

1. Uso del comparador : cuando sea necesario usar la lógica de clasificación en varios lugares Si desea usar la lógica de clasificación en un solo lugar, puede escribir una clase interna anónima de la siguiente manera, o bien extraer el comparador y usarlo en varios lugares

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

Podemos tener una comprobación nula de las propiedades, si pudiéramos haber usado 'Long' en lugar de 'long'.

2. Uso de Comparable (ordenamiento natural) : si el algoritmo de clasificación siempre se adhiere a una propiedad: escriba una clase que implemente el método 'Comparable' y anule el método 'compareTo' como se define a continuación

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

}

llame al método de clasificación para ordenar según el orden natural

Collections.sort(list);
Jagadeesh
fuente
7

En java8 + esto se puede escribir en una sola línea de la siguiente manera:

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

código:

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

o

ListOfActiveAlarmObj.sort(Comparator.comparing(ActiveAlarm::getTimeStarted))
Karthikeyan
fuente
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;
        }
 }

Eso debería darte una idea aproximada. Una vez hecho esto, puede llamar Collections.sort()a la lista.

Kal
fuente
4

Desde Java8 esto se puede hacer aún más limpio usando una combinación de ComparatoryLambda expressions

Por ejemplo:

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.
fuente
2

Cadena de comparación de guayaba :

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();
            }});
卢 声 远 Shengyuan Lu
fuente
1

En Java necesitas usar el Collections.sortmétodo estático . Aquí hay un ejemplo de una lista de objetos CompanyRole, ordenados primero por comienzo y luego por fin. Puede adaptarse fácilmente a su propio objeto.

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
fuente
0

Puede llamar a Collections.sort () y pasar un Comparador que necesita escribir para comparar diferentes propiedades del objeto.

Liv
fuente
0

Como se mencionó, puede ordenar por:

  • Hacer que su objeto se implemente Comparable
  • O pasar un ComparatoraCollections.sort

Si hace ambas cosas, Comparablese ignorará y Comparatorse utilizará. Esto ayuda a que los objetos de valor tengan su propia lógica, Comparableque es el tipo más razonable para su objeto de valor, mientras que cada caso de uso individual tiene su propia implementación.

Alireza Fattahi
fuente