Saltar al contenido

Cómo comparar objetos por múltiples campos

Este enunciado fue analizado por nuestros expertos así se garantiza la exactitud de esta sección.

Solución:

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

deberías implementar Comparable . Suponiendo que todos los campos no serán null (en aras de la simplicidad), esa edad es un int, y la clasificación de comparación es primero, último, edad, el compareTo mé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);

(de Formas de ordenar listas de objetos en Java en función de varios 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 con 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 enrevesado: clasificar a mano

Collections.sort(pizzas, new Comparator()   
    @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 al usar Strings en su lugar (sin seguridad de tipos, refactorizaciones automáticas). Ahora, si se cambia el nombre de un campo, el compilador ni siquiera informará de 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: Clasificación con ComparisonChain de Google Guava

Collections.sort(pizzas, new Comparator()   
    @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: null-los valores deben valorarse menos por defecto. Para null-fields, 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, null).

Clasificación con Apache Commons CompareToBuilder

Collections.sort(pizzas, new Comparator()   
    @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 ordena fácilmente en varios campos, pero también define el comportamiento predeterminado para null valores (es decir, 1, a, b, z, null). Sin embargo, tampoco puede especificar nada más, a menos que proporcione su propio comparador.

Por lo tanto

En última instancia, todo se reduce al sabor y la necesidad de flexibilidad (ComparisonChain de Guava) frente a código conciso (CompareToBuilder de Apache).

método de bonificación

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

class MultiComparator implements Comparator 
    private final List> comparators;

    public MultiComparator(List> comparators) 
        this.comparators = comparators;
    

    public MultiComparator(Comparator... comparators) 
        this(Arrays.asList(comparators));
    

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

    public static  void sort(List list, Comparator... comparators) 
        Collections.sort(list, new MultiComparator(comparators));
    

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

ComparatorUtils.chainedComparator(comparatorCollection)

Collections.sort(list, ComparatorUtils.chainedComparator(comparators));

Si te ha sido de ayuda nuestro artículo, agradeceríamos que lo compartas con más seniors de esta forma contrubuyes a extender nuestro contenido.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)


Tags : / /

Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *