Uma tarefa comum no dia a dia dos desenvolvedores é ordenar uma lista ou array. Para não inventar a roda, a Collections API do Java (também conhecida pelo nome do seu pacote, o java.util) vem pronta para ajudar nessa tarefa. Falamos dessa API extensivamente na apostila do curso FJ-11, e vou aqui passar para o problema específico de ordenação.
Imagine que você gostaria de ordenar uma lista de Contas bancárias. Cada conta possui um número (int) e um titular (String):
O método para ordenar uma lista se encontra na classe java.util.Collections (repare o “s” no final). Ela possui métodos estáticos que ajudam a manipular coleções, entre eles o método sort. Assim podemos tentar ordenar a lista de contas:
Mas infelizmente a linha acima nem compila. Antes de invocar o método sort é preciso definir o critério de ordenação: uma forma de informar, dado duas contas, qual vem “antes” e qual vem “depois”.
Se o número da conta atual é menor do que da outraConta retormamos -1 (ou qualquer int negativo, indicando que this deve vir “antes” de outraConta), se for maior retornamos 1 (ou qualquer int positivo) e se for igual então devolvemos 0.
Mas se surgir a necessidade de ordenar pelo titular da conta? Não queremos alterar o método compareTo na classe Conta, já que isso mudaria a ordem natural. Queremos definir um outro critério de ordenação. Para tal, existe uma outra interface: a Comparator:
Vamos então implementar a interface para definir a ordem pelo titular (String) da conta. Comparar duas Strings é difícil, mas, como você pode imaginar, esse problema já foi resolvido na API do Java.
A classe String já sabe compara dois strings, sabemos isso pois ela implementa a interface Comparable (por esse mesmo motivo podemos invocar Collections.sort para uma List de String). Podemos então delegar essa tarefa ao método compareTo das Strings:
Como escolher para que esse critério de comparação seja utilizado em vez da ordem natural? O método sort é sobrecarregado e pode receber um objeto do tipo Comparator:
Falta mencionar que o método compareTo da interface Comparable deve ser consistente com o método equals. Quando uma conta é igual a outra (a classe Conta sobreescreve o método equals para definir igualdade), o método compareTo deve devolver zero também. Devemos também pensar se receberemos null como Contas para ordenar, e tomar as devidas precauções nos comparadores.
Através de Comparable e Comparator também são controladas a ordenação da coleção TreeSet e as chaves do mapa TreeMap.
Vale lembrar que muita coisa mudou desde esse post, com a entrada do Java 8, e você pode ler sobre lambdas e outras facilidades que ajudam em funções como comparação aqui no blog da Caelum.
Em um trecho você diz que a interface Comparable é do pacote java.util, e na verdade ela é do java.lang
@William excelente codigo! é realmente bastante util, da para aumentar ainda mais sua ideia e criar uma fluent interface para ainda configurar casos de empate, etc…
primeiramente parabéns pela explicação, cara eu fiz exatamente como está no seu post, tenho um arrayList e quero ordená-lo sempre de acordo com um atributo dos objetos que estao no array, na classe de dominio implementei a comparable e fiz o compareTo com o atributo que será levado em conta na ordenação do mesmo jeito que vc fez no post(meu atributo também é um int) após isso chamei a collections.sort passando meu array de objetos e o array continuou com a mesma ordenação, faltou eu fazer alguma coisa? obrigado
Vendo no exemplo que TitularComparator é uma public class e lembrando da static nested class CaseInsensitiveComparator usada na classe String, me veio a dúvida se há uma solução melhor entre as duas ou se depende do caso. Dúvida também válida para o exemplo de enum do William, externo ou interno à uma classe?
Sobre usar uma classe aninhada estatica, é interessante para deixar claro que aquilo só é pra ser usado com a classe String, e para nao poluir o pacote com classes pequenas… mas é bastante questionável. Uma classe publica top level teria o mesmo sentido (e é minha preferencia na maioria dos casos).
O metodo abstrato é desnecessário mesmo, e o tipo realmente deve ser parametrizado (Acho que no post do tobe nao apareceu porque o html comeu o sinal de maior e de menor) senao o metodo compare receberia dois Objects.
como faço para ordenar essa listaCaixas pelo atributo volume da classe Caixa…e tem que ser da caixa com maior volume para a que tiver menor volume..o compareTo não está aceitando tipo double.
na verdade a sua classe caixa tem q implementar comparable ai vc comprar dentro do metodo compareTo, mas procure no GUJ la vc vai encontrar mais informação
Agora, se você já tem os vários Comparators e que quiser agrupá-los e/ou reordená-los, a classe Ordering tem um método estático chamado compound que faz exatamente isso:
O que eu não entendi é porque tem um método abstrato em um enum, não faz com que ele também seja abstrato, sabendo que um Enum jamais poderia ser abastrato
Oi Bruno! uma enum pode sim ter metodo abstrato, bastando voce implementar aquele metodo em todas as enums da enum.
Excelente artigo! Utilizei em um código e funcionou perfeitamente, inclusive consegui utilizar vários critérios para gerar uma tabela de classificação de equipes. Ficou dessa forma:
Fiz o curso presencial FJ-11 da Caelum, mas não consigo de forma alguma entender isso de Camparator vs Comparable, QUE SACO!
Pense assim: Comparable é que você quer o código de ordenação dentro da propria classe. String e Integer implementam Comparable. Por isso nao precisam de Comparator para fazer o sort ou usar TreeSet.
POREM, se quisermos ordená-los em uma outra ordem, precisaremos ter um Comparator. Tanto o Collections.sort quanto o construtor de TreeSet recebem Comparators. Assim você vai poder ordenar uma ordem diferente da “ordem natural” definida pela implementação de Comparable que eles tem.
Obrigado obrigado muito obrigado :3 tava fazendo um joguinho aqui e precisava classificar a posição Z da Sprite e isso veio a caiar