Quando usar comparável e comparador

108

Tenho uma lista de objetos que preciso classificar em um campo, digamos Pontuação. Sem pensar muito, escrevi uma nova classe que implementa Comparator, que faz a tarefa e funciona.

Agora, olhando para trás, estou me perguntando se eu deveria ter feito minha classe implementar Comparable em vez de criar uma nova classe que implementasse Comparator. A pontuação é o único campo em que os objetos serão ordenados.

  1. O que fiz é aceitável como prática?

  2. A abordagem certa é "Primeiro, faça com que a classe implemente Comparable (para a ordem natural) e, se uma comparação de campo alternativa for necessária, crie uma nova classe que implemente Comparator"?

  3. Se (2) acima for verdadeiro, então isso significa que se deve implementar Comparator apenas depois de ter a classe implementar Comparable? (Supondo que eu possua a classe original).

pkrish
fonte

Respostas:

80

Eu diria que um objeto deve implementar Comparable se essa for a maneira clara e natural de classificar a classe, e qualquer pessoa que precise classificar a classe geralmente deseja fazer isso dessa forma.

Se, no entanto, a classificação foi um uso incomum da classe, ou se a classificação só faz sentido para um caso de uso específico, um Comparador é uma opção melhor.

Colocado de outra forma, dado o nome da classe, está claro como uma comparável seria classificada ou você precisa recorrer à leitura do javadoc? Se for o último, as probabilidades são de que cada caso de uso de classificação futuro exigiria um comparador, ponto em que a implementação de comparável pode desacelerar os usuários da classe, não acelerá-los.

Yishai
fonte
Você pode dar um exemplo rápido?
rgamber
este pode ser um bom exemplo: gist.github.com/yclian/2627608 Há uma classe de versão que usa ComparableVersion. Versão - fornece métodos de fábrica ComparableVersion supostamente um objeto (sem métodos estáticos) - fornece uma versão que pode ser comparada com outra. As responsabilidades são separadas.
ses
2
você pode referir java-journal.blogspot.in/2011/01/…
a Learner
O entrevistador perguntou por que usar o Comparator quando o mesmo pode ser feito com o Comparable, e eu fui burro :(
Aadam de
@aLearner link está morto
G.Brown
127

Usar Comparable se quiser definir um padrão comportamento de ordenação (natural) do objeto em questão, uma prática comum é usar um identificador técnico ou natural (banco de dados?) Do objeto para isso.

Use Comparatorse quiser definir um comportamento de pedido externo controlável ; isso pode substituir o comportamento de pedido padrão.

BalusC
fonte
3
Essa é uma explicação técnica, e por mais correta que possa parecer, mas não diz nada sobre as melhores práticas.
extraneon
40
está dizendo quando usar cada um - se essa não for uma prática recomendada, o que é?
Bozho
1
"Implementar Comparablesignifica que estou definindo a ordem natural?" , isso me deu a resposta que eu procurava. Obrigado :)
Somjit
61

Use Comparable:

  • se o objeto estiver sob seu controle.
  • se o comportamento de comparação é o comportamento de comparação principal.

Use Comparator:

  • se o objeto está fora do seu controle e você não pode fazê-los implementar Comparable .
  • quando você deseja comparar um comportamento diferente do comportamento padrão (que é especificado por Comparable).
Bozho
fonte
20

Comparável -java.lang.Comparable: int compareTo(Object o1)

Um objeto comparável é capaz de se comparar a outro objeto. A própria classe deve implementar a interface java.lang.Comparable para poder comparar suas instâncias.

  • Capaz de comparar o objeto atual com o objeto fornecido.
  • Usando isso, podemos implementar only one sort sequence base nas propriedades das instâncias. EX:Person.id
  • Algumas das classes predefinidas como String, classes Wrapper, Date, Calendar implementaram a interface Comparable.

Comparador -java.util.Comparator: int compare(Object o1, Object o2)

Um objeto comparador é capaz de comparar dois objetos diferentes. A classe não está comparando suas instâncias, mas as instâncias de algumas outras classes. Esta classe comparadora deve implementar a interface java.util.Comparator.

  • Capaz de comparar quaisquer dois objetos do mesmo tipo.
  • Usando isso, podemos implementar many sort sequence e nomear cada um, com base nas propriedades das instâncias. EX:Person.id, Person.name, Person.age
  • Podemos implementar a interface Comparator para nossas classes predefinidas para classificação personalizada.

Exemplo:

public class Employee implements Comparable<Employee> {

    private int id;
    private String name;
    private int age;
    private long salary;

    // Many sort sequences can be created with different names.
    public static Comparator<Employee> NameComparator = new Comparator<Employee>() {         
        @Override
        public int compare(Employee e1, Employee e2) {
            return e1.getName().compareTo(e2.getName());
        }
    };
    public static Comparator<Employee> idComparator = new Comparator<Employee>() {       
        @Override
        public int compare(Employee e1, Employee e2) {
            return Integer.valueOf(e1.getId()).compareTo(Integer.valueOf(e2.getId()));
        }
    };

    public Employee() { }
    public Employee(int id, String name, int age, long salary){
        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
    }
    // setters and getters.

    // Only one sort sequence can be created with in the class.
    @Override
    public int compareTo(Employee e) {
    //return Integer.valueOf(this.id).compareTo(Integer.valueOf(e.id));
    //return Character.toString(this.name.charAt(0)).compareToIgnoreCase(Character.toString(e.name.charAt(0)));
        if (this.id > e.id) {
            return 1;
        }else if(this.id < e.id){
            return -1;
        }else {
            return Character.toString(this.name.charAt(0)).compareToIgnoreCase(Character.toString(e.name.charAt(0)));
        }

    }   

    public static void main(String[] args) {

        Employee e1 = new Employee(5, "Yash", 22, 1000);
        Employee e2 = new Employee(8, "Tharun", 24, 25000);

        List<Employee> list = new ArrayList<Employee>();
        list.add(e1);
        list.add(e2);
        Collections.sort(list); // call @compareTo(o1)
        Collections.sort(list, Employee.nameComparator); // call @compare (o1,o2)
        Collections.sort(list, Employee.idComparator); // call @compare (o1,o2)
    }
}
  • Para a classificação personalizada, usamos o comparador @compare (o1, o2). Para outros cenários, usamos o comparador @compareTo (o1), sem alterar o código se quisermos classificar mais de um campo, então usamos o comparador.

Para Java 8 Lambda: Comparator consulte meu post.

Yash
fonte
10

Comparable deve ser usado quando você compara instâncias da mesma classe.

Comparator pode ser usado para comparar instâncias de classes diferentes.

Comparable é implementado por classes que precisam definir uma ordem natural para seus objetos. Como String implementa Comparable.

No caso de alguém desejar uma ordem de classificação diferente, ele pode implementar o comparador e definir sua própria maneira de comparar duas instâncias.

Devang Paliwal
fonte
10

Se a classificação de objetos precisar ser baseada na ordem natural, use Comparable, enquanto se a classificação precisar ser feita em atributos de objetos diferentes, use Comparator em Java.

Principais diferenças entre Comparável e Comparador:

+------------------------------------------------------------------------------------+
¦               Comparable                ¦                Comparator                ¦
¦-----------------------------------------+------------------------------------------¦
¦ java.lang.Comparable                    ¦ java.util.Comparator                     ¦
¦-----------------------------------------+------------------------------------------¦
¦ int objOne.compareTo(objTwo)            ¦ int compare(objOne, objTwo)              ¦
¦-----------------------------------------+------------------------------------------¦
¦ Negative, if objOne < objTwo            ¦ Same as Comparable                       ¦
¦ Zero,  if objOne == objTwo              ¦                                          ¦
¦ Positive,  if objOne > objTwo           ¦                                          ¦
¦-----------------------------------------+------------------------------------------¦
¦ You must modify the class whose         ¦ You build a class separate from to sort. ¦
¦ instances you want to sort.             ¦ the class whose instances you want       ¦
¦-----------------------------------------+------------------------------------------¦
¦ Only one sort sequence can be created   ¦ Many sort sequences can be created       ¦
¦-----------------------------------------+------------------------------------------¦
¦ Implemented frequently in the API by:   ¦ Meant to be implemented to sort          ¦
¦ String, Wrapper classes, Date, Calendar ¦ instances of third-party classes.        ¦
+------------------------------------------------------------------------------------+
Joby Wilson Mathews
fonte
9

O Comparator faz tudo o que o comparador faz e muito mais.

| | Comparable | Comparator ._______________________________________________________________________________ Is used to allow Collections.sort to work | yes | yes Can compare multiple fields | yes | yes Lives inside the class you’re comparing and serves | | as a “default” way to compare | yes | yes Can live outside the class you’re comparing | no | yes Can have multiple instances with different method names | no | yes Input arguments can be a list of | just Object| Any type Can use enums | no | yes

Achei a melhor abordagem para usar comparadores como classes anônimas da seguinte maneira:

private static void sortAccountsByPriority(List<AccountRecord> accounts) {
    Collections.sort(accounts, new Comparator<AccountRecord>() {

        @Override
        public int compare(AccountRecord a1, AccountRecord a2) {
            return a1.getRank().compareTo(a2.getRank());
        }
    });
}

Você pode criar várias versões de tais métodos dentro da classe que está planejando classificar. Então você pode ter:

  • sortAccountsByPriority
  • sortAccountsByType
  • sortAccountsByPriorityAndType

    etc ...

Agora, você pode usar esses métodos de classificação em qualquer lugar e obter a reutilização do código. Isso me dá tudo o que um comparável teria, e mais ... então não vejo nenhuma razão para usar comparável.


fonte
8

Eu diria:

  • se a comparação for intuitiva, implemente o Comparable
  • se não estiver claro se sua comparação é intuitiva, use um Comparador, pois é mais explícito e, portanto, mais claro para a pobre alma que tem que manter o código
  • se houver mais de uma comparação intuitiva possível, prefiro um Comparador, possivelmente criado por um método de fábrica na classe a ser comparada.
  • se a comparação for um propósito especial, use o Comparator
extrânon
fonte
6

Os pontos a seguir o ajudam a decidir em quais situações se deve usar Comparable e em qual Comparator:

1) Disponibilidade do código

2) Critérios de classificação único versus múltiplo

3) Arays.sort () e Collection.sort ()

4) Como chaves em SortedMap e SortedSet

5) Mais número de classes versus flexibilidade

6) Comparações entre classes

7) Ordem Natural

Para um artigo mais detalhado, você pode consultar Quando usar comparável e quando usar comparador

um aprendiz
fonte
Eu me pergunto por que ninguém está votando a favor dessa resposta. É muito bom. +1
Diganta
4
  • Se no momento de escrever a classe você tiver apenas um caso de uso de classificação, use Comparável.
  • Somente quando você tiver mais de uma estratégia de classificação, implemente um Comparador.
fabrizioM
fonte
4

Se você precisar de classificação de ordem natural - Comparável com o usuário SE precisar de classificação de ordem personalizada - Use o comparador

Exemplo:

Class Employee{
private int id;
private String name;
private String department;
}

A ordenação por ordem natural seria baseada no id porque seria única e a ordenação por ordem personalizada seria o nome e o departamento.

Refrences:
Quando uma classe deve ser comparável e / ou comparadora? http://javarevisited.blogspot.com/2011/06/comparator-and-comparable-in-java.html

Rajiv
fonte
3

Houve uma pergunta semelhante aqui: Quando uma classe deve ser comparável e / ou comparadora?

Eu diria o seguinte: Implemente comparável para algo como uma ordem natural, por exemplo, com base em um ID interno

Implemente um comparador se você tiver um algoritmo de comparação mais complexo, por exemplo, campos múltiplos e assim por diante.

ZeissS
fonte
1
Encomendar em vários campos também pode ser feito com Comparable.
BalusC de
Para a diferença entre comparável e comparador, você pode consultar java-journal.blogspot.in/2010/12/…
a Learner
2

Comparável:
sempre que quisermos armazenar apenas elementos homogêneos e a ordem de classificação natural padrão necessária, podemos ir para a implementação de classe comparable interface de .

Comparador:
Sempre que quisermos armazenar elementos homogêneos e heterogêneos e quisermos classificar na ordem de classificação personalizada padrão, podemos ir para a comparatorinterface.

G swamy reddy
fonte
0

Minha necessidade era classificar com base na data.

Então, usei o Comparable e funcionou facilmente para mim.

public int compareTo(GoogleCalendarBean o) {
    // TODO Auto-generated method stub
    return eventdate.compareTo(o.getEventdate());
}

Uma restrição com Comparable é que eles não podem ser usados ​​para Coleções que não sejam List.

primavera profissional
fonte
0

Se você possui a classe, é melhor ir com Comparable . Geralmente Comparador é usado se você não possuir a classe, mas você tem que usá-lo uma TreeSet ou TreeMap porque Comparador pode ser passado como um parâmetro no conctructor de TreeSet ou TreeMap. Você pode ver como usar Comparator e Comparable em http://preciselyconcise.com/java/collections/g_comparator.php

Sai Sunder
fonte
0

Fui questionado sobre a classificação de um intervalo definido de números em tempo melhor do que o normal em uma das entrevistas. (Não usa classificação por contagem)

A implementação da interface Comparable sobre um objeto permite que os algoritmos de classificação implícitos usem o método compareTo substituído para ordenar os elementos de classificação e isso seria o tempo linear.

Senhor maravilhoso
fonte
0

Comparable é a ordem de classificação natural padrão fornecida para valores numéricos em ordem crescente e para strings em ordem alfabética. por exemplo:

Treeset t=new Treeset();
t.add(2);
t.add(1);
System.out.println(t);//[1,2]

Comparator é a ordem de classificação personalizada implementada na classe myComparator personalizada, substituindo um método de comparação por exemplo:

Treeset t=new Treeset(new myComparator());
t.add(55);
t.add(56);
class myComparator implements Comparator{
public int compare(Object o1,Object o2){
//Descending Logic
}
}
System.out.println(t);//[56,55]
Ajay Takur
fonte
-1

Uma abordagem muito simples é assumir que a classe de entidade em questão seja representada no banco de dados e, em seguida, na tabela do banco de dados, você precisaria de um índice composto de campos da classe de entidade? Se a resposta for sim, implemente comparável e use o (s) campo (s) de índice para ordenação natural. Em todos os outros casos, use comparador.

user976715
fonte
-2

Minha biblioteca de anotações para implementação Comparablee Comparator:

public class Person implements Comparable<Person> {         
    private String firstName;  
    private String lastName;         
    private int age;         
    private char gentle;         

    @Override         
    @CompaProperties({ @CompaProperty(property = "lastName"),              
        @CompaProperty(property = "age",  order = Order.DSC) })           
    public int compareTo(Person person) {                 
        return Compamatic.doComparasion(this, person);         
    }  
}

Clique no link para ver mais exemplos. http://code.google.com/p/compamatic/wiki/CompamaticByExamples

Jianmin Liu
fonte