Cómo comparar objetos por múltiples campos

237

Suponga que tiene algunos objetos que tienen varios campos con los que se pueden comparar:

public class Person {

    private String firstName;
    private String lastName;
    private String age;

    /* Constructors */

    /* Methods */

}

Entonces, en este ejemplo, cuando preguntas si:

a.compareTo(b) > 0

Tal vez se pregunte si el apellido de a viene antes que el de b, o si a es anterior a b, etc.

¿Cuál es la forma más limpia de permitir la comparación múltiple entre este tipo de objetos sin agregar desorden innecesario o sobrecarga?

  • java.lang.Comparable la interfaz permite la comparación solo por un campo
  • Agregar numerosos métodos de comparación (es decir compareByFirstName(), compareByAge()etc.) está abarrotado en mi opinión.

Entonces, ¿cuál es la mejor manera de hacerlo?

Yuval Adam
fuente
3
¿Por qué es esto un CW? Es una pregunta de programación perfectamente válida.
Elie
2
¿Sabe que Comparable permite la comparación en tantos campos como desee?
DJClayworth

Respuestas:

81

Puede implementar un Comparatorque compara dos Personobjetos, y puede examinar tantos campos como desee. Puede poner una variable en su comparador que le indique con qué campo comparar, aunque probablemente sería más simple escribir comparadores múltiples.

Elie
fuente
55
En realidad prefiero la idea de usar un solo comparador. No creo que esta respuesta sea incorrecta, pero cualquiera que la lea definitivamente debería verificar la respuesta de Steve Kuo a continuación.
Felipe Leão
Los comparadores múltiples eran solo si deseaba métodos de comparación diferentes que no fueran función de los datos en sí mismos, es decir, a veces desea comparar por nombre, otras por edad, etc. Para comparar por múltiples campos al mismo tiempo, solo un comparador Sería necesario.
Elie
399

Con Java 8:

Comparator.comparing((Person p)->p.firstName)
          .thenComparing(p->p.lastName)
          .thenComparingInt(p->p.age);

Si tiene métodos de acceso:

Comparator.comparing(Person::getFirstName)
          .thenComparing(Person::getLastName)
          .thenComparingInt(Person::getAge);

Si una clase implementa Comparable, dicho comparador puede usarse en el método compareTo:

@Override
public int compareTo(Person o){
    return Comparator.comparing(Person::getFirstName)
              .thenComparing(Person::getLastName)
              .thenComparingInt(Person::getAge)
              .compare(this, o);
}
Enigo
fuente
55
Especialmente el reparto (Person p)es importante para los comparadores encadenados.
membersound
55
¿Qué tan eficiente es cuando se compara una gran cantidad de objetos, por ejemplo, al ordenar? ¿Tiene que crear nuevas Comparatorinstancias en cada llamada?
jjurm
44
Obtengo una NullPointerException cuando uno de los campos que estoy comparando es nulo, como una Cadena. ¿Hay alguna forma de mantener este formato de comparación pero permitir que sea nulo seguro?
llega
3
@jjurm .thenComparing(Person::getLastName, Comparator.nullsFirst(Comparator.naturalOrder()))- primer selector de campo, luego comparador
gavenkoa
2
@jjurm cuando lo usa compareTocomo se muestra arriba, Comparatorse crea cada vez que se llama al método. Puede evitar esto almacenando el comparador en un campo final estático privado.
Gandalf
165

Deberías implementar Comparable <Person>. Suponiendo que todos los campos no serán nulos (por simplicidad), esa edad es un int, y comparar la clasificación es primero, último, edad, el compareTométodo es bastante simple:

public int compareTo(Person other) {
    int i = firstName.compareTo(other.firstName);
    if (i != 0) return i;

    i = lastName.compareTo(other.lastName);
    if (i != 0) return i;

    return Integer.compare(age, other.age);
}
Steve Kuo
fuente
10
si implementa Comparable <Person>, entonces el método es compareTo (Persona p) ... parece que esta respuesta se mezcló con el método Comparator's compare <T o1, T o2>
Mike
55
Esto no es recomendable. Use Comparator cuando tenga múltiples campos.
indika
1
esa es la mejor solución en este momento, (mejor que más comparadores)
Vasile Surdu
44
@indika, tengo curiosidad: ¿por qué no se recomienda esto? Comparar usando más de una propiedad me parece perfectamente bien.
ars-longa-vita-brevis
44
@ ars-longa-vita-brevis, si utiliza Comparable, la lógica de clasificación debe estar en la misma clase cuyos objetos se ordenan, por lo que esto se denomina ordenación natural de los objetos. Con el uso de Comparator puede escribir una lógica de clasificación personalizada fuera de la clase Person. Si desea comparar objetos Persona solo por su nombre o apellido, no puede usar esta lógica. Tienes que escribirlo de nuevo,
indika
78

(desde Formas de ordenar listas de objetos en Java en función de múltiples campos )

Código de trabajo en esta esencia

Uso de Java 8 lambda (agregado el 10 de abril de 2019)

Java 8 resuelve esto muy bien por lambda (aunque Guava y Apache Commons aún podrían ofrecer más flexibilidad):

Collections.sort(reportList, Comparator.comparing(Report::getReportKey)
            .thenComparing(Report::getStudentNumber)
            .thenComparing(Report::getSchool));

Gracias a la respuesta de @ gaoagong a continuación .

Desordenado y complicado: ordenar a mano

Collections.sort(pizzas, new Comparator<Pizza>() {  
    @Override  
    public int compare(Pizza p1, Pizza p2) {  
        int sizeCmp = p1.size.compareTo(p2.size);  
        if (sizeCmp != 0) {  
            return sizeCmp;  
        }  
        int nrOfToppingsCmp = p1.nrOfToppings.compareTo(p2.nrOfToppings);  
        if (nrOfToppingsCmp != 0) {  
            return nrOfToppingsCmp;  
        }  
        return p1.name.compareTo(p2.name);  
    }  
});  

Esto requiere mucho tipeo, mantenimiento y es propenso a errores.

La forma reflexiva: ordenar con BeanComparator

ComparatorChain chain = new ComparatorChain(Arrays.asList(
   new BeanComparator("size"), 
   new BeanComparator("nrOfToppings"), 
   new BeanComparator("name")));

Collections.sort(pizzas, chain);  

Obviamente, esto es más conciso, pero aún más propenso a errores, ya que pierde su referencia directa a los campos mediante el uso de cadenas (sin seguridad de tipografía, refactorizaciones automáticas). Ahora, si se cambia el nombre de un campo, el compilador ni siquiera informará un problema. Además, debido a que esta solución utiliza la reflexión, la clasificación es mucho más lenta.

Cómo llegar: Ordenando con Google Guava's ComparisonChain

Collections.sort(pizzas, new Comparator<Pizza>() {  
    @Override  
    public int compare(Pizza p1, Pizza p2) {  
        return ComparisonChain.start().compare(p1.size, p2.size).compare(p1.nrOfToppings, p2.nrOfToppings).compare(p1.name, p2.name).result();  
        // or in case the fields can be null:  
        /* 
        return ComparisonChain.start() 
           .compare(p1.size, p2.size, Ordering.natural().nullsLast()) 
           .compare(p1.nrOfToppings, p2.nrOfToppings, Ordering.natural().nullsLast()) 
           .compare(p1.name, p2.name, Ordering.natural().nullsLast()) 
           .result(); 
        */  
    }  
});  

Esto es mucho mejor, pero requiere un código de placa de caldera para el caso de uso más común: los valores nulos deben valorarse menos por defecto. Para los campos nulos, debe proporcionar una directiva adicional a Guava sobre qué hacer en ese caso. Este es un mecanismo flexible si desea hacer algo específico, pero a menudo desea el caso predeterminado (es decir, 1, a, b, z, nulo).

Ordenar con Apache Commons CompareToBuilder

Collections.sort(pizzas, new Comparator<Pizza>() {  
    @Override  
    public int compare(Pizza p1, Pizza p2) {  
        return new CompareToBuilder().append(p1.size, p2.size).append(p1.nrOfToppings, p2.nrOfToppings).append(p1.name, p2.name).toComparison();  
    }  
});  

Al igual que ComparisonChain de Guava, esta clase de biblioteca se clasifica fácilmente en múltiples campos, pero también define el comportamiento predeterminado para valores nulos (es decir, 1, a, b, z, nulo). Sin embargo, tampoco puede especificar nada más, a menos que proporcione su propio Comparador.

Así

En última instancia, se trata de sabor y la necesidad de flexibilidad (Guava's ComparisonChain) versus código conciso (Apache's CompareToBuilder).

Método de bonificación

Encontré una buena solución que combina múltiples comparadores en orden de prioridad en CodeReview en MultiComparator:

class MultiComparator<T> implements Comparator<T> {
    private final List<Comparator<T>> comparators;

    public MultiComparator(List<Comparator<? super T>> comparators) {
        this.comparators = comparators;
    }

    public MultiComparator(Comparator<? super T>... comparators) {
        this(Arrays.asList(comparators));
    }

    public int compare(T o1, T o2) {
        for (Comparator<T> c : comparators) {
            int result = c.compare(o1, o2);
            if (result != 0) {
                return result;
            }
        }
        return 0;
    }

    public static <T> void sort(List<T> list, Comparator<? super T>... comparators) {
        Collections.sort(list, new MultiComparator<T>(comparators));
    }
}

Por supuesto, Apache Commons Collections ya tiene una utilidad para esto:

ComparatorUtils.chainedComparator (comparatorCollection)

Collections.sort(list, ComparatorUtils.chainedComparator(comparators));
Benny Bottema
fuente
22

@Patrick Para ordenar más de un campo consecutivamente intente ComparatorChain

Un ComparatorChain es un Comparador que envuelve uno o más Comparadores en secuencia. ComparatorChain llama a cada Comparador en secuencia hasta que 1) cualquier Comparador devuelve un resultado distinto de cero (y ese resultado se devuelve), o 2) ComparatorChain se agota (y se devuelve cero). Este tipo de ordenación es muy similar a la ordenación de varias columnas en SQL, y esta clase permite que las clases Java emulen ese tipo de comportamiento al ordenar una Lista.

Para facilitar aún más la ordenación de tipo SQL, el orden de cualquier Comparador individual en la lista se puede invertir.

Llamar a un método que agrega nuevos comparadores o cambia el orden de ascenso / descenso después de que se haya llamado a comparar (Objeto, Objeto) dará como resultado una excepción de operación no admitida. Sin embargo, tenga cuidado de no alterar la Lista de comparadores subyacente o el BitSet que define el orden de clasificación.

Las instancias de ComparatorChain no están sincronizadas. La clase no es segura para subprocesos en el momento de la construcción, pero es segura para subprocesos para realizar comparaciones múltiples después de que se completen todas las operaciones de configuración.

Nigel_V_Thomas
fuente
20

Otra opción que siempre puede considerar es Apache Commons. Proporciona muchas opciones.

import org.apache.commons.lang3.builder.CompareToBuilder;

Ex:

public int compare(Person a, Person b){

   return new CompareToBuilder()
     .append(a.getName(), b.getName())
     .append(a.getAddress(), b.getAddress())
     .toComparison();
}
Xeroiris
fuente
10
import com.google.common.collect.ComparisonChain;

/**
 * @author radler
 * Class Description ...
 */
public class Attribute implements Comparable<Attribute> {

    private String type;
    private String value;

    public String getType() { return type; }
    public void setType(String type) { this.type = type; }

    public String getValue() { return value; }
    public void setValue(String value) { this.value = value; }

    @Override
    public String toString() {
        return "Attribute [type=" + type + ", value=" + value + "]";
    }

    @Override
    public int compareTo(Attribute that) {
        return ComparisonChain.start()
            .compare(this.type, that.type)
            .compare(this.value, that.value)
            .result();
    }

}
Ran Adler
fuente
1
Me gusta mucho esta estrategia. ¡Gracias!
Sr. Polywhirl
La forma más efectiva! Gracias
Zakaria Bouazza
8

Para aquellos que pueden usar la API de transmisión Java 8, hay un enfoque más ordenado que está bien documentado aquí: Lambdas y clasificación

Estaba buscando el equivalente de C # LINQ:

.ThenBy(...)

Encontré el mecanismo en Java 8 en el Comparador:

.thenComparing(...)

Así que 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 de arriba para obtener una forma más ordenada y una explicación sobre cómo la inferencia de tipos de Java hace que sea un poco más difícil de definir en comparación con LINQ.

Aquí está la prueba de unidad completa para 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
fuente
7

Escribir un Comparatormanual para tal caso de uso es una terrible solución IMO. Dichos enfoques ad hoc tienen muchos inconvenientes:

  • Sin reutilización de código. Viola SECO.
  • Repetitivo.
  • Mayor posibilidad de errores.

Entonces, ¿cuál es la solución?

Primero algo de teoría.

Denotemos la proposición "tipo Aadmite comparación" por Ord A. (Desde la perspectiva del programa, puede pensar Ord Acomo un objeto que contiene lógica para comparar dos As. Sí, al igual que Comparator).

Ahora, si Ord Ay Ord B, entonces su compuesto (A, B)también debería admitir la comparación. es decir Ord (A, B). Si Ord A, Ord By Ord C, entonces Ord (A, B, C).

Podemos extender este argumento a la aridad arbitraria y decir:

Ord A, Ord B, Ord C, ..., Ord ZOrd (A, B, C, .., Z)

Llamemos a esta declaración 1.

La comparación de los compuestos funcionará tal como lo describió en su pregunta: primero se intentará la primera comparación, luego la siguiente, luego la siguiente, y así sucesivamente.

Esa es la primera parte de nuestra solución. Ahora la segunda parte.

Si sabe que Ord A, y sabe cómo transformar Ba A(llamada que función de transformación f), entonces también puede tener Ord B. ¿Cómo? Bueno, cuando Bse comparan las dos instancias, primero las transforma para Ausar fy luego se aplica Ord A.

Aquí, estamos mapeando la transformación B → Aa Ord A → Ord B. Esto se conoce como mapeo contravariante (o comappara abreviar).

Ord A, (B → A)comap Ord B

Llamemos a esta declaración 2.


Ahora apliquemos esto a su ejemplo.

Tiene un tipo de datos denominado Personque comprende tres campos de tipo String.

  • Sabemos que Ord String. Por la declaración 1, Ord (String, String, String).

  • Podemos escribir fácilmente una función de Persona (String, String, String). (Simplemente devuelva los tres campos). Como sabemos Ord (String, String, String)y Person → (String, String, String), mediante la declaración 2, podemos usar comappara obtener Ord Person.

QED


¿Cómo implemento todos estos conceptos?

La buena noticia es que no tienes que hacerlo. Ya existe una biblioteca que implementa todas las ideas descritas en esta publicación. (Si tiene curiosidad sobre cómo se implementan, puede mirar debajo del capó ).

Así es como se verá el código con él:

Ord<Person> personOrd = 
 p3Ord(stringOrd, stringOrd, stringOrd).comap(
   new F<Person, P3<String, String, String>>() {
     public P3<String, String, String> f(Person x) {
       return p(x.getFirstName(), x.getLastname(), x.getAge());
     }
   }
 );

Explicación:

  • stringOrdEs un objeto de tipo Ord<String>. Esto corresponde a nuestra propuesta original de "comparación de apoyos".
  • p3Ordes un método que toma Ord<A>, Ord<B>, Ord<C>, y vuelve Ord<P3<A, B, C>>. Esto corresponde a la declaración 1. ( P3significa producto con tres elementos. Producto es un término algebraico para compuestos).
  • comapcorresponde a bien comap,.
  • F<A, B>representa una función de transformación A → B.
  • p es un método de fábrica para crear productos.
  • La expresión completa corresponde a la declaración 2.

Espero que ayude.

missingfaktor
fuente
5

En lugar de métodos de comparación, es posible que desee definir varios tipos de subclases de "Comparador" dentro de la clase Persona. De esa manera, puede pasarlos a los métodos de clasificación de Colecciones estándar.

Marc Novakowski
fuente
3

Creo que sería más confuso si su algoritmo de comparación fuera "inteligente". Me gustaría ir con los numerosos métodos de comparación que sugirió.

La única excepción para mí sería la igualdad. Para las pruebas unitarias, me ha sido útil anular los .Equals (en .net) para determinar si varios campos son iguales entre dos objetos (y no que las referencias sean iguales).

Michael Haren
fuente
3

Si hay varias formas en que un usuario puede ordenar a una persona, también puede tener varias configuraciones de Comparador como constantes en alguna parte. La mayoría de las operaciones de ordenación y colecciones ordenadas toman un comparador como parámetro.

sblundy
fuente
3
//Following is the example in jdk 1.8
package com;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

class User {
    private String firstName;
    private String lastName;
    private Integer age;

    public Integer getAge() {
        return age;
    }

    public User setAge(Integer age) {
        this.age = age;
        return this;
    }

    public String getFirstName() {
        return firstName;
    }

    public User setFirstName(String firstName) {
        this.firstName = firstName;
        return this;
    }

    public String getLastName() {
        return lastName;
    }

    public User setLastName(String lastName) {
        this.lastName = lastName;
        return this;
    }

}

public class MultiFieldsComparision {

    public static void main(String[] args) {
        List<User> users = new ArrayList<User>();

        User u1 = new User().setFirstName("Pawan").setLastName("Singh").setAge(38);
        User u2 = new User().setFirstName("Pawan").setLastName("Payal").setAge(37);
        User u3 = new User().setFirstName("Anuj").setLastName("Kumar").setAge(60);
        User u4 = new User().setFirstName("Anuj").setLastName("Kumar").setAge(43);
        User u5 = new User().setFirstName("Pawan").setLastName("Chamoli").setAge(44);
        User u6 = new User().setFirstName("Pawan").setLastName("Singh").setAge(5);

        users.add(u1);
        users.add(u2);
        users.add(u3);
        users.add(u4);
        users.add(u5);
        users.add(u6);

        System.out.println("****** Before Sorting ******");

        users.forEach(user -> {
            System.out.println(user.getFirstName() + " , " + user.getLastName() + " , " + user.getAge());
        });

        System.out.println("****** Aftre Sorting ******");

        users.sort(
                Comparator.comparing(User::getFirstName).thenComparing(User::getLastName).thenComparing(User::getAge));

        users.forEach(user -> {
            System.out.println(user.getFirstName() + " , " + user.getLastName() + " , " + user.getAge());
        });

    }

}
Pawan
fuente
3

La implementación del código de la misma está aquí si tenemos que ordenar el objeto Persona en función de múltiples campos.

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class Person {

private String firstName;
private String lastName;
private int age;

public String getFirstName() {
    return firstName;
}

public void setFirstName(String firstName) {
    this.firstName = firstName;
}

public String getLastName() {
    return lastName;
}

public void setLastName(String lastName) {
    this.lastName = lastName;
}

public int getAge() {
    return age;
}

public void setAge(int age) {
    this.age = age;
}

public Person(String firstName, String lastName, int age) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
}


static class PersonSortingComparator implements Comparator<Person> {

    @Override
    public int compare(Person person1, Person person2) {

        // for first name comparison
        int firstNameCompare = person1.getFirstName().compareTo(person2.getFirstName());

        // for last name comparison
        int lastNameCompare = person1.getLastName().compareTo(person2.getLastName());

        // for last name comparison
        int ageCompare = person1.getAge() - person2.getAge();

        // Now comparing
        if (firstNameCompare == 0) {
            if (lastNameCompare == 0) {
                return ageCompare;
            }
            return lastNameCompare;
        }
        return firstNameCompare;
    }
}

public static void main(String[] args) {
    Person person1 = new Person("Ajay", "Kumar", 27);
    Person person2 = new Person("Ajay","Gupta", 23);
    Person person3 = new Person("Ajay","Kumar", 22);


    ArrayList<Person> persons = new ArrayList<>();
    persons.add(person1);
    persons.add(person2);
    persons.add(person3);


    System.out.println("Before Sorting:\n");
    for (Person person : persons) {
        System.out.println(person.firstName + " " + person.lastName + " " + person.age);
    }

    Collections.sort(persons, new PersonSortingComparator());

    System.out.println("After Sorting:\n");
    for (Person person : persons) {
        System.out.println(person.firstName + " " + person.lastName + " " + person.age);
    }
}

}
rev. Sachchit Bansal
fuente
2
//here threshold,buyRange,targetPercentage are three keys on that i have sorted my arraylist 
final Comparator<BasicDBObject> 

    sortOrder = new Comparator<BasicDBObject>() {
                    public int compare(BasicDBObject e1, BasicDBObject e2) {
                        int threshold = new Double(e1.getDouble("threshold"))
                        .compareTo(new Double(e2.getDouble("threshold")));
                        if (threshold != 0)
                            return threshold;

                        int buyRange = new Double(e1.getDouble("buyRange"))
                        .compareTo(new Double(e2.getDouble("buyRange")));
                        if (buyRange != 0)
                            return buyRange;

                        return (new Double(e1.getDouble("targetPercentage")) < new Double(
                                e2.getDouble("targetPercentage")) ? -1 : (new Double(
                                        e1.getDouble("targetPercentage")) == new Double(
                                                e2.getDouble("targetPercentage")) ? 0 : 1));
                    }
                };
                Collections.sort(objectList, sortOrder);
Pradeep Singh
fuente
Llegué a esta pregunta porque mi código comenzó a gustarme tu respuesta;)
Groth
0

Si implementa el Comparable interfaz , querrá elegir una propiedad simple para ordenar. Esto se conoce como ordenamiento natural. Piense en ello como el valor predeterminado. Siempre se usa cuando no se suministra un comparador específico. Por lo general, este es el nombre, pero su caso de uso puede requerir algo diferente. Puede utilizar cualquier número de otros comparadores que pueda suministrar a varias API de colecciones para anular el orden natural.

También tenga en cuenta que, por lo general, si a.compareTo (b) == 0, entonces a.equals (b) == verdadero. Está bien si no, pero hay efectos secundarios a tener en cuenta. Vea los excelentes javadocs en la interfaz Comparable y encontrará mucha información excelente sobre esto.

Mark Renouf
fuente
0

Siguiente blog dado buen ejemplo comparador encadenado

http://www.codejava.net/java-core/collections/sorting-a-list-by-multiple-attributes-example

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

/**
 * This is a chained comparator that is used to sort a list by multiple
 * attributes by chaining a sequence of comparators of individual fields
 * together.
 *
 */
public class EmployeeChainedComparator implements Comparator<Employee> {

    private List<Comparator<Employee>> listComparators;

    @SafeVarargs
    public EmployeeChainedComparator(Comparator<Employee>... comparators) {
        this.listComparators = Arrays.asList(comparators);
    }

    @Override
    public int compare(Employee emp1, Employee emp2) {
        for (Comparator<Employee> comparator : listComparators) {
            int result = comparator.compare(emp1, emp2);
            if (result != 0) {
                return result;
            }
        }
        return 0;
    }
}

Comparador de llamadas:

Collections.sort(listEmployees, new EmployeeChainedComparator(
                new EmployeeJobTitleComparator(),
                new EmployeeAgeComparator(),
                new EmployeeSalaryComparator())
        );
revs vaquar khan
fuente
0

A partir de la respuesta de Steve, se puede utilizar el operador ternario:

public int compareTo(Person other) {
    int f = firstName.compareTo(other.firstName);
    int l = lastName.compareTo(other.lastName);
    return f != 0 ? f : l != 0 ? l : Integer.compare(age, other.age);
}
revs Gerold Broser
fuente
0

Es fácil comparar dos objetos con el método hashcode en java`

public class Sample{

  String a=null;
  String b=null;

  public Sample(){
      a="s";
      b="a";
  }
  public Sample(String a,String b){
      this.a=a;
      this.b=b;
  }
  public static void main(String args[]){
      Sample f=new Sample("b","12");
      Sample s=new Sample("b","12");
      //will return true
      System.out.println((s.a.hashCode()+s.b.hashCode())==(f.a.hashCode()+f.b.hashCode()));

      //will return false
      Sample f=new Sample("b","12");
      Sample s=new Sample("b","13");
      System.out.println((s.a.hashCode()+s.b.hashCode())==(f.a.hashCode()+f.b.hashCode()));

}
éxodo
fuente
Por favor no hagas eso. Los códigos hash no deben usarse para comparaciones de igualdad sino para indexación de tablas hash. Las colisiones de hash pueden dar como resultado la igualdad de dos objetos diferentes. Incluso las tablas hash dependen de la igualdad real si se producen colisiones hash.
Noel Widmer
0

Por lo general, anulo mi compareTo()método de esta manera cada vez que tengo que hacer una clasificación multinivel.

public int compareTo(Song o) {
    // TODO Auto-generated method stub
    int comp1 = 10000000*(movie.compareTo(o.movie))+1000*(artist.compareTo(o.artist))+songLength;
    int comp2 = 10000000*(o.movie.compareTo(movie))+1000*(o.artist.compareTo(artist))+o.songLength;
    return comp1-comp2;
} 

Aquí primero se da preferencia al nombre de la película, luego al artista y, por último, a songLength. Solo debes asegurarte de que esos multiplicadores estén lo suficientemente distantes como para no cruzar los límites del otro.

jayanth
fuente