Como comparar objetos por vários campos


237

Suponha que você tenha alguns objetos com vários campos que podem ser comparados por:

public class Person {

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

    /* Constructors */

    /* Methods */

}

Portanto, neste exemplo, quando você pergunta se:

a.compareTo(b) > 0

você pode estar se perguntando se o sobrenome de a vem antes de b, ou se a é mais antigo que b, etc ...

Qual é a maneira mais limpa de permitir a comparação múltipla entre esses tipos de objetos sem adicionar sobrecarga desnecessária ou sobrecarga?

  • java.lang.Comparable interface permite a comparação apenas por um campo
  • Adicionando vários métodos comparar (ou seja compareByFirstName(), compareByAge(), etc ...) está cheio na minha opinião.

Então, qual é a melhor maneira de fazer isso?


3
por que isso é um cw? É uma questão de programação perfeitamente válida.
Elie

2
Você está ciente de que o Comparable permite a comparação por quantos campos você quiser?
DJClayworth

Respostas:


81

Você pode implementar um Comparatorque compara dois Personobjetos e pode examinar quantos campos desejar. Você pode inserir uma variável em seu comparador que diga a qual campo comparar, embora provavelmente seja mais simples escrever vários comparadores.


5
Na verdade, prefiro a ideia de usar um único comparador. Eu não acho que esta resposta esteja errada, mas qualquer pessoa que a leia deve definitivamente conferir a resposta de Steve Kuo abaixo.
Felipe Leão

Os comparadores múltiplos foram apenas se você quiser métodos de comparação diferentes que não sejam uma função dos dados em si - ou seja, às vezes você deseja comparar por nome, outras vezes por idade, etc. Para comparar por vários campos ao mesmo tempo, apenas um comparador seria necessário.
Elie

399

Com o Java 8:

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

Se você tiver métodos de acessador:

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

Se uma classe implementar Comparable, esse comparador poderá ser usado no método compareTo:

@Override
public int compareTo(Person o){
    return Comparator.comparing(Person::getFirstName)
              .thenComparing(Person::getLastName)
              .thenComparingInt(Person::getAge)
              .compare(this, o);
}

5
Especialmente o elenco (Person p)é importante para comparadores encadeados.
membersound

5
Qual é a eficiência na comparação de um grande número de objetos, por exemplo, na classificação? Ele precisa criar novas Comparatorinstâncias em todas as chamadas?
Jjurm

4
Eu recebo uma NullPointerException quando um dos campos que estou comparando é nulo, como uma String. Existe alguma maneira de manter esse formato de comparação, mas permitir que ele seja nulo seguro?
rveach

3
@jjurm .thenComparing(Person::getLastName, Comparator.nullsFirst(Comparator.naturalOrder()))- primeiro selector de campo, então o comparador
gavenkoa

2
@jjurm quando você o usa, compareTocomo mostrado acima, Comparatoré criado sempre que o método é chamado. Você pode evitar isso armazenando o comparador em um campo final estático privado.
Gandalf #

165

Você deve implementar Comparable <Person>. Supondo que todos os campos não serão nulos (por uma questão de simplicidade), que a idade é um int e a classificação da comparação é a primeira, a última, a idade, o compareTométodo é bastante simples:

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

10
se você implementar Comparable <Person>, em seguida, o método é compareTo (Person p) .. parece que esta resposta foi misturado com o Comparador comparar <T o1, o2 T> método
Mike

5
Isto não é recomendado. Use o Comparador quando você tiver vários campos.
Indika #

1
essa é a melhor solução no momento, (melhor do que mais comparadores)
Vasile Surdu

4
@ indika, estou curioso: por que isso não é recomendado? Comparar usando mais de uma propriedade me parece perfeitamente bem.
ars-longa-vita-brevis

4
@ ars-longa-vita-brevis, Se você estiver usando Comparable, a lógica de classificação deve estar na mesma classe cujos objetos estão sendo classificados, de modo que isso é chamado de ordenação natural de objetos. Com o uso do Comparator, você pode escrever uma lógica de classificação personalizada fora da classe Person. Se você deseja comparar objetos Pessoa apenas pelo nome ou sobrenome, não poderá usar essa lógica. Você tem que escrever de novo,
Indika

78

(de maneiras de classificar listas de objetos em Java com base em vários campos )

Código de trabalho nesta essência

Usando Java 8 lambda's (adicionado em 10 de abril de 2019)

O Java 8 resolve isso muito bem com o lambda (embora Guava e Apache Commons ainda possam oferecer mais flexibilidade):

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

Graças à resposta de @ gaoagong abaixo .

Confuso e complicado: Classificação manual

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

Isso requer muita digitação, manutenção e é propenso a erros.

A maneira reflexiva: Classificando com BeanComparator

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

Collections.sort(pizzas, chain);  

Obviamente, isso é mais conciso, mas ainda mais suscetível a erros, pois você perde sua referência direta aos campos usando Strings (sem segurança tipográfica, auto-refatorações). Agora, se um campo for renomeado, o compilador nem relatará um problema. Além disso, como essa solução usa reflexão, a classificação é muito mais lenta.

Como chegar: Classificando com o ComparisonChain do Google Guava

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

Isso é muito melhor, mas requer algum código da placa da caldeira para o caso de uso mais comum: valores nulos devem ter menos valor por padrão. Para campos nulos, você deve fornecer ao Guava uma diretiva extra o que fazer nesse caso. Esse é um mecanismo flexível se você deseja fazer algo específico, mas geralmente deseja o caso padrão (por exemplo, 1, a, b, z, null).

Classificação com o 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();  
    }  
});  

Como o ComparisonChain do Guava, essa classe de biblioteca classifica facilmente em vários campos, mas também define o comportamento padrão para valores nulos (ou seja, 1, a, b, z, null). No entanto, você também não pode especificar mais nada, a menos que você forneça seu próprio Comparador.

portanto

Em última análise, tudo se resume ao sabor e à necessidade de flexibilidade (ComparavaChain da Guava) versus código conciso (CompareToBuilder do Apache).

Método bônus

Encontrei uma boa solução que combina vários comparadores em ordem de prioridade no CodeReview em 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));
    }
}

Claro que o Apache Commons Collections já tem um utilitário para isso:

ComparatorUtils.chainedComparator (comparatorCollection)

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

22

@Patrick Para classificar mais de um campo consecutivamente, tente ComparatorChain

Um ComparatorChain é um comparador que agrupa um ou mais comparadores em sequência. O ComparatorChain chama cada Comparator em sequência até 1) qualquer comparador retornar um resultado diferente de zero (e esse resultado é retornado) ou 2) o ComparatorChain se esgotar (e zero é retornado). Esse tipo de classificação é muito semelhante à classificação de várias colunas no SQL, e essa classe permite que as classes Java emulem esse tipo de comportamento ao classificar uma Lista.

Para facilitar ainda mais a classificação do tipo SQL, a ordem de qualquer Comparador único na lista pode ser revertida.

Chamar um método que adiciona novos comparadores ou altera a classificação ascendente / descendente após a comparação (Objeto, Objeto) ter sido chamada resultará em UnsupportedOperationException. No entanto, tome cuidado para não alterar a lista de comparadores subjacente ou o BitSet que define a ordem de classificação.

Instâncias do ComparatorChain não são sincronizadas. A classe não é segura para threads no momento da construção, mas é segura para executar várias comparações depois que todas as operações de configuração estiverem concluídas.


20

Outra opção que você sempre pode considerar é o Apache Commons. Ele oferece muitas opções.

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


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

}

1
Eu gosto muito dessa estratégia. Obrigado!
Mr. Polywhirl

Maneira mais eficaz! Obrigado
Zakaria Bouazza

8

Para aqueles capazes de usar a API de streaming Java 8, existe uma abordagem mais limpa que está bem documentada aqui: Lambdas e classificação

Eu estava procurando o equivalente do C # LINQ:

.ThenBy(...)

Encontrei o mecanismo no Java 8 no comparador:

.thenComparing(...)

Então, aqui está o trecho que demonstra o algoritmo.

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

Confira o link acima para obter uma maneira mais clara e uma explicação sobre como a inferência de tipo do Java torna um pouco mais difícil de definir em comparação com o LINQ.

Aqui está o teste de unidade completo para referência:

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

7

Escrever um Comparatormanual para esse caso de uso é uma solução IMO terrível. Tais abordagens ad hoc têm muitas desvantagens:

  • Sem reutilização de código. Viola SECO.
  • Chapa de ebulição.
  • Maior possibilidade de erros.

Então, qual é a solução?

Primeiro alguma teoria.

Vamos denotar a proposição "tipo Asuporta comparação" por Ord A. (Da perspectiva do programa, você pode pensar Ord Aem um objeto que contém lógica para comparar dois As. Sim, assim comoComparator .)

Agora, se Ord Ae Ord B, então, seu composto (A, B)também deve suportar comparação. ie Ord (A, B). Se Ord A,, Ord Be Ord C, então Ord (A, B, C).

Podemos estender esse argumento à aridade arbitrária e dizer:

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

Vamos chamar esta afirmação 1.

A comparação dos compostos funcionará exatamente como você descreveu na sua pergunta: a primeira comparação será tentada primeiro, depois a próxima, depois a próxima e assim por diante.

Essa é a primeira parte da nossa solução. Agora a segunda parte.

Se você sabe disso Ord Ae sabe como se transformar Bem A(chame essa função de transformação f), também pode ter Ord B. Quão? Bem, quando as duas Binstâncias devem ser comparadas, primeiro você as transforma em Auso fe depois aplicaOrd A .

Aqui, estamos mapeando a transformação B → Apara Ord A → Ord B. Isso é conhecido como mapeamento contravariante (oucomap abreviado).

Ord A, (B → A)comap Ord B

Vamos chamar esta afirmação 2.


Agora vamos aplicar isso ao seu exemplo.

Você tem um tipo de dados nomeado Personque compreende três campos do tipo String.

  • Nós sabemos disso Ord String. Pela declaração 1 Ord (String, String, String),.

  • Podemos escrever facilmente uma função de Personpara (String, String, String). (Apenas retorne os três campos.) Como sabemos Ord (String, String, String)e Person → (String, String, String), pela declaração 2, podemos usar o comapget Ord Person.

QED.


Como faço para implementar todos esses conceitos?

A boa notícia é que você não precisa. Já existe uma biblioteca que implementa todas as idéias descritas neste post. (Se você está curioso para saber como eles são implementados, pode ver por baixo do capô .)

É assim que o código ficará com ele:

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

Explicação:

  • stringOrdé um objecto do tipo Ord<String>. Isso corresponde à nossa proposta original de "comparação de suportes".
  • p3Ordé um método que leva Ord<A>, Ord<B>, Ord<C>, e retorna Ord<P3<A, B, C>>. Isso corresponde à afirmação 1. ( P3significa produto com três elementos. Produto é um termo algébrico para compósitos.)
  • comapcorresponde a bem comap,.
  • F<A, B>representa uma função de transformação A → B.
  • p é um método de fábrica para criar produtos.
  • A expressão inteira corresponde à afirmação 2.

Espero que ajude.


5

Em vez de métodos de comparação, você pode definir apenas vários tipos de subclasses "Comparator" dentro da classe Person. Dessa forma, você pode passá-los para os métodos de classificação de coleções padrão.


3

Eu acho que seria mais confuso se o seu algoritmo de comparação fosse "inteligente". Eu iria com os numerosos métodos de comparação que você sugeriu.

A única exceção para mim seria a igualdade. Para testes de unidade, foi útil substituir os .Equals (em .net) para determinar se vários campos são iguais entre dois objetos (e não que as referências sejam iguais).


3

Se houver várias maneiras de um usuário solicitar uma pessoa, você também poderá ter várias configurações do Comparator como constantes em algum lugar. A maioria das operações de classificação e coleções classificadas usa um comparador como parâmetro.


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

    }

}

3

A implementação de código do mesmo está aqui se precisarmos classificar o objeto Person com base em vários 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);
    }
}

}

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

Eu vim para esta pergunta porque o meu código comecei a gostar de sua resposta;)
Jan Groth

0

Se você implementar a interface Comparável , escolha uma propriedade simples para ordenar. Isso é conhecido como ordenamento natural. Pense nisso como o padrão. É sempre usado quando nenhum comparador específico é fornecido. Geralmente esse é o nome, mas seu caso de uso pode exigir algo diferente. Você pode usar qualquer número de outros comparadores que possa fornecer a várias APIs de coleções para substituir a ordem natural.

Observe também que normalmente se a.compareTo (b) == 0, então a.equals (b) == true. Tudo bem se não, mas existem efeitos colaterais a serem observados. Veja os excelentes javadocs na interface Comparable e você encontrará muitas informações excelentes sobre isso.


0

Blog a seguir, com um bom exemplo de Comparador encadeado

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 chamadas:

Collections.sort(listEmployees, new EmployeeChainedComparator(
                new EmployeeJobTitleComparator(),
                new EmployeeAgeComparator(),
                new EmployeeSalaryComparator())
        );

0

A partir da resposta de Steve, o operador ternário pode ser usado:

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

0

É fácil comparar dois objetos com o método hashcode em 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()));

}

Por favor, não faça isso. Os códigos de hash não devem ser usados ​​para comparações de igualdade, mas para indexação de hashtable. As colisões de hash podem resultar em igualdade para dois objetos diferentes. Até as hashtables dependem da igualdade real se ocorrerem colisões de hash.
precisa

0

Normalmente, substituo meu compareTo()método dessa maneira sempre que tenho que fazer uma classificação multinível.

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

Aqui, a primeira preferência é dada ao nome do filme, depois ao artista e, finalmente, ao songLength. Você só precisa garantir que esses multiplicadores estejam distantes o suficiente para não cruzar os limites um do outro.


Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.