¿Cómo ordenar por dos campos en Java?

Resuelto Damir asked hace 13 años • 15 respuestas

Tengo una variedad de objetos person (int age; String name;).

¿Cómo puedo ordenar esta matriz alfabéticamente por nombre y luego por edad?

¿Qué algoritmo usarías para esto?

Damir avatar Jan 26 '11 21:01 Damir
Aceptado

Puedes utilizar Collections.sortlo siguiente:

private static void order(List<Person> persons) {

    Collections.sort(persons, new Comparator() {

        public int compare(Object o1, Object o2) {

            String x1 = ((Person) o1).getName();
            String x2 = ((Person) o2).getName();
            int sComp = x1.compareTo(x2);

            if (sComp != 0) {
               return sComp;
            } 

            Integer x1 = ((Person) o1).getAge();
            Integer x2 = ((Person) o2).getAge();
            return x1.compareTo(x2);
    }});
}

List<Persons>Ahora está ordenado por nombre y luego por edad.

String.compareTo"Compara dos cadenas lexicográficamente" - de los documentos .

Collections.sortes un método estático en la biblioteca de Colecciones nativa. Realiza la clasificación real, solo necesita proporcionar un Comparador que defina cómo se deben comparar dos elementos en su lista: esto se logra proporcionando su propia implementación del comparemétodo.

Richard H avatar Jan 26 '2011 14:01 Richard H

Para aquellos que pueden utilizar la API de streaming de Java 8, existe un enfoque más sencillo que está bien documentado aquí: Lambdas y clasificación

Estaba buscando el equivalente de C# LINQ:

.ThenBy(...)

Encontré el mecanismo en Java 8 en Comparator:

.thenComparing(...)

Aquí está el fragmento que demuestra el algoritmo.

    Comparator<Person> comparator = Comparator.comparing(person -> person.name);
    comparator = comparator.thenComparing(Comparator.comparing(person -> person.age));

Consulte el enlace anterior para ver una forma más clara y una explicación sobre cómo la inferencia de tipos de Java hace que su definición sea un poco más complicada en comparación con LINQ.

Aquí está la prueba unitaria completa como referencia:

@Test
public void testChainedSorting()
{
    // Create the collection of people:
    ArrayList<Person> people = new ArrayList<>();
    people.add(new Person("Dan", 4));
    people.add(new Person("Andi", 2));
    people.add(new Person("Bob", 42));
    people.add(new Person("Debby", 3));
    people.add(new Person("Bob", 72));
    people.add(new Person("Barry", 20));
    people.add(new Person("Cathy", 40));
    people.add(new Person("Bob", 40));
    people.add(new Person("Barry", 50));

    // Define chained comparators:
    // Great article explaining this and how to make it even neater:
    // http://blog.jooq.org/2014/01/31/java-8-friday-goodies-lambdas-and-sorting/
    Comparator<Person> comparator = Comparator.comparing(person -> person.name);
    comparator = comparator.thenComparing(Comparator.comparing(person -> person.age));

    // Sort the stream:
    Stream<Person> personStream = people.stream().sorted(comparator);

    // Make sure that the output is as expected:
    List<Person> sortedPeople = personStream.collect(Collectors.toList());
    Assert.assertEquals("Andi",  sortedPeople.get(0).name); Assert.assertEquals(2,  sortedPeople.get(0).age);
    Assert.assertEquals("Barry", sortedPeople.get(1).name); Assert.assertEquals(20, sortedPeople.get(1).age);
    Assert.assertEquals("Barry", sortedPeople.get(2).name); Assert.assertEquals(50, sortedPeople.get(2).age);
    Assert.assertEquals("Bob",   sortedPeople.get(3).name); Assert.assertEquals(40, sortedPeople.get(3).age);
    Assert.assertEquals("Bob",   sortedPeople.get(4).name); Assert.assertEquals(42, sortedPeople.get(4).age);
    Assert.assertEquals("Bob",   sortedPeople.get(5).name); Assert.assertEquals(72, sortedPeople.get(5).age);
    Assert.assertEquals("Cathy", sortedPeople.get(6).name); Assert.assertEquals(40, sortedPeople.get(6).age);
    Assert.assertEquals("Dan",   sortedPeople.get(7).name); Assert.assertEquals(4,  sortedPeople.get(7).age);
    Assert.assertEquals("Debby", sortedPeople.get(8).name); Assert.assertEquals(3,  sortedPeople.get(8).age);
    // Andi     : 2
    // Barry    : 20
    // Barry    : 50
    // Bob      : 40
    // Bob      : 42
    // Bob      : 72
    // Cathy    : 40
    // Dan      : 4
    // Debby    : 3
}

/**
 * A person in our system.
 */
public static class Person
{
    /**
     * Creates a new person.
     * @param name The name of the person.
     * @param age The age of the person.
     */
    public Person(String name, int age)
    {
        this.age = age;
        this.name = name;
    }

    /**
     * The name of the person.
     */
    public String name;

    /**
     * The age of the person.
     */
    public int age;

    @Override
    public String toString()
    {
        if (name == null) return super.toString();
        else return String.format("%s : %d", this.name, this.age);
    }
}
Luke Machowski avatar Nov 11 '2014 12:11 Luke Machowski

Utilizando el enfoque Java 8 Streams, con referencias de métodos en los captadores...

// Create a stream...
var sortedList = persons.stream()
    // sort it (does not sort the original list)...
    .sorted(Comparator.comparing(Person::getName)
                      .thenComparing(Person::getAge));
    // and collect to a new list
    .collect(Collectors.toList());

También es posible la recopilación en una matriz:

persons.stream()
    .sorted(Comparator.comparing(Person::getName)
                      .thenComparing(Person::getAge));
    .toArray(String[]::new);

Y el enfoque de Java 8 Lambda...

//Sorts the original list Lambda style
persons.sort((p1, p2) -> {
        if (p1.getName().compareTo(p2.getName()) == 0) {
            return p1.getAge().compareTo(p2.getAge());
        } else {
            return p1.getName().compareTo(p2.getName());
        } 
    });

Por último...

// This syntax is similar to the Streams example above, but sorts the original list!!!
persons.sort(Comparator.comparing(Person::getName).thenComparing(Person::getAge));
Bradley D avatar May 23 '2016 00:05 Bradley D