Comparator

ae pessoal ja abrir um topico uma vez sobre essa interface e ja fiz buscas aqui no guj… li mais nao conseguir ainda entender a essencia dessa classe.nem como implementa-la de forma bem simples e funcional… fiz com a interface comparable e me dei ate bem… veja…

import java.util.ArrayList;
import java.util.Collections;
class Dog implements Comparable<Dog>{
	String nome;
	Dog(String n){
		nome=n;
	}
	public String getNome(){
		return nome;
	}
	public String toString(){
	return nome;}
	
	public static void main(String arg[]){
		Dog d = new Dog("bela");
		Dog d1 = new Dog("aoop");
		Dog d3 = new Dog("aiko");
		Dog d4 = new Dog("baz");
		ArrayList<Dog> animal = new ArrayList<Dog>();
			animal.add(d);
			animal.add(d1);
			animal.add(d3);
			animal.add(d4);
		Collections.sort(animal);
		System.out.println(animal.toString());
	}
	public int compareTo(Dog dd){
		return nome.compareTo(dd.getNome());
	}
}

mas com comparator as coisas nao anda nao… quem puder me dar um help novamente ai… agradeco!! mesmo…
e pq esse nao compila :?:

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

class Cat implements Comparable<Cat>{
	int id;
	Cat(int a){
		id=a;
	}
	public int getId(){
		return id;
	}
	public static void main(String args[]){
		Cat c = new Cat(20);
		Cat c1 = new Cat(4);
		Cat c2 = new Cat(8);	
	ArrayList<Cat> cat = new ArrayList<Cat>();
		cat.add(c);
		cat.add(c1);
		cat.add(c2);
	Collections.sort(cat);
	System.out.println(cat);
	
	}
	public int compareTo(Cat ct){
		return id.compareTo(ct.getId());
	}	
}

Vc implementará a interface Comparator separadamenteo, para poder passar essa classe ao método sort(obj, ClasseImplComparator).

Acredito que a essência das interfaces Comparable e Comparator sejam idênticas, porém usando o Comparator, vc não precisará alterar a classe, como teria que fazer se usasse o Comparable. A classe que implementa Comparator é criada separadamente.

Acredito que a implementação deve ser idêntica para Comparable e Comparator, pois ambas tem a mesma lógica.

Desculpe-me a confusão … para mim esse assunto é relativamente novo.

Att.
Fernando.

Você usa Comparable quando quer implementar a comparação internamente aos objetos, e usa Comparator quando quer implementar a comparação de forma externa.

Exemplo:

public interface FiguraGeometrica{
    public double getArea();
}

public class Circulo implements FiguraGeometrica, Comparable<FiguraGeometrica> {
    public double getArea(){
       //...
    }
    public int compareTo(FiguraGeometrica outraFigura){
        return new Double(getArea()).compareTo(outraFigura.getArea());
    }
}

public class Retangulo implements FiguraGeometrica, Comparable<FiguraGeometrica>{
//...
}

public class Triangulo implements FiguraGeometrica, Comparable<FiguraGeometrica>{
//...
}

public class ComparadorEsdruxulo implements Comparator<FiguraGeometrica> {
    public int compareTo(FiguraGeometrica figura1, FiguraGeometrica figura2){
        if(figura1 instanceof Triangulo) return new Double(figura1.getArea() * 1.2).compareTo(figura2.getArea());

        return new Double(figura1.getArea()).compareTo(figura2.getArea());
    }
}

public class MasterOfPuppets{
    public static void main(String[] args){
        List<FiguraGeometrica> figuras = new ArrayList<FiguraGeometrica();
        // Popula a lista
        Collections.sort(figuras);
        for(FiguraGeometrica f : figuras) System.out.println(f);
        Collections.sort(figuras, new ComparadorEsdruxulo());
        for(FiguraGeometrica f: figuras) System.out.println(f);
    }
}

Cada figura implementa Comparable e a primeira ordenação ordena as figuras de acordo com a área de cada uma. A segunda ordenação ordena as figuras de acordo com a área também, exceto se uma delas for um triângulo, daí ele considera a área com mais 20% (não, não faz sentido. Pra falar a verdade, acho que nem funciona direito, pois eu não estou considerando a hipótese de figura2 ser um triângulo).

Você também poderia, neste meu exemplo, não implementar Comparable em cada figura e criar um Comparator que simplesmente compare a área. Isso seria legal porque não iria duplicar o código de comparar a área em cada classe que implementa FiguraGeometrica.

Estou partindo do pressuposto que você saiba trabalhar minimamente com generics, ok?
Bom, existem duas interfaces nessa brincadeira: A Comparable e a Comparator. Ambas têm o propósito de estabelecer uma relação de ordenação entre objetos. Por exemplo: Vamos supor que você cria um uma classe Quadrado

01 public class Quadrado { 02 private int aresta; 03 04 public Quadrado(int aresta) { 05 if(aresta <= 0) 06 throw new IllegalArgumentException("aresta = " + aresta); 07 this.aresta = aresta; 08 } 09 10 public int getAresta() { 11 return aresta; 12 } 13 14 public long getArea() { 15 return (long)Math.pow(aresta, 2); 16 } 17 18 public double getHipotenusa() { 19 return Math.sqrt(2 * Math.pow(aresta, 2)); 20 } 21 22 public boolean equals(Object obj) { 23 if(!(obj instanceof Quadrado)) 24 return false; 25 26 Quadrado outro = (Quadrado)obj; 27 28 return aresta == outro.aresta; 29 } 30 31 public String toString() { 32 return 33 "[aresta=" + aresta + 34 "; area=" + getArea() + 35 "; hipotenusa=" + getHipotenusa() + "]" 36 ; 37 } 38 }
Esta é uma classe bem simples, só pra representar um quadrado mesmo. Essa classe disponibiliza um mecanismo que nos permite verificar se dois objetos do tipo Quadrado são equivalentes ou não: O método equals.
Vamos supor agora que queremos agora estabelecer uma ordenação entre quadrados. Digamos que nosso critério seja o seguinte:
:arrow: Um quadrado q1 é maior que um quadrado q2, se a aresta de q1 for maior que a aresta de q2.
:arrow: Um quadrado q1 é menor que um quadrado q2, se a aresta de q1 for menor que a aresta de q2.
:arrow: Um quadrado q1 é igual a um quadrado q2, se a aresta de q1 for igual a aresta de q2.
Mas… essa classe por acaso disponibiliza algum mecanismo que nos permita descobrir se um determinado objeto Quadrado é maior ou menor que um outro objeto Quadrado? A resposta é não. Para podermos verificar se um quadrado é maior ou menor que o outro, segundo o critério mencionado acima, nós mesmos teriamos que fazer algum algoritmozinho pra determinar quando um Quadrado é menor ou maior que outro:

01 public class TesteQuadrado { 02 public static void main(String[] args) { 03 Quadrado[] quadrados = { 04 new Quadrado(1), 05 new Quadrado(35), 06 new Quadrado(12), 07 new Quadrado(35), 08 new Quadrado(12), 09 new Quadrado(1), 10 }; 11 12 for(int quad = 0; quad < quadrados.length; quad++) { 13 System.out.println("O quadrado " + quadrados[quad] + " é "); 14 for (int cmp = 0; cmp < quadrados.length; cmp++) { 15 String comparacao; 16 if(quadrados[quad].getAresta() > quadrados[cmp].getAresta()) 17 comparacao = " maior que "; 18 else 19 if(quadrados[quad].getAresta() < quadrados[cmp].getAresta()) 20 comparacao = " menor que "; 21 else 22 comparacao = " igual a"; 23 24 System.out.println( 25 "\t" + comparacao + "o quadrado " + quadrados[cmp] 26 ); 27 } 28 } 29 } 30 }
Este programa pega um punhado de objetos do tipo Quadrado e compara-os entre si, exibindo na tela a conclusão das comparações. A saída fica assim:

O quadrado [aresta=1; area=1; hipotenusa=1.4142135623730951] é 
	 igual ao quadrado [aresta=1; area=1; hipotenusa=1.4142135623730951]
	 menor que o quadrado [aresta=35; area=1225; hipotenusa=49.49747468305833]
	 menor que o quadrado [aresta=12; area=144; hipotenusa=16.97056274847714]
	 menor que o quadrado [aresta=35; area=1225; hipotenusa=49.49747468305833]
	 menor que o quadrado [aresta=12; area=144; hipotenusa=16.97056274847714]
	 igual ao quadrado [aresta=1; area=1; hipotenusa=1.4142135623730951]
O quadrado [aresta=35; area=1225; hipotenusa=49.49747468305833] é 
	 maior que o quadrado [aresta=1; area=1; hipotenusa=1.4142135623730951]
	 igual ao quadrado [aresta=35; area=1225; hipotenusa=49.49747468305833]
	 maior que o quadrado [aresta=12; area=144; hipotenusa=16.97056274847714]
	 igual ao quadrado [aresta=35; area=1225; hipotenusa=49.49747468305833]
	 maior que o quadrado [aresta=12; area=144; hipotenusa=16.97056274847714]
	 maior que o quadrado [aresta=1; area=1; hipotenusa=1.4142135623730951]
O quadrado [aresta=12; area=144; hipotenusa=16.97056274847714] é 
	 maior que o quadrado [aresta=1; area=1; hipotenusa=1.4142135623730951]
	 menor que o quadrado [aresta=35; area=1225; hipotenusa=49.49747468305833]
	 igual ao quadrado [aresta=12; area=144; hipotenusa=16.97056274847714]
	 menor que o quadrado [aresta=35; area=1225; hipotenusa=49.49747468305833]
	 igual ao quadrado [aresta=12; area=144; hipotenusa=16.97056274847714]
	 maior que o quadrado [aresta=1; area=1; hipotenusa=1.4142135623730951]
O quadrado [aresta=35; area=1225; hipotenusa=49.49747468305833] é 
	 maior que o quadrado [aresta=1; area=1; hipotenusa=1.4142135623730951]
	 igual ao quadrado [aresta=35; area=1225; hipotenusa=49.49747468305833]
	 maior que o quadrado [aresta=12; area=144; hipotenusa=16.97056274847714]
	 igual ao quadrado [aresta=35; area=1225; hipotenusa=49.49747468305833]
	 maior que o quadrado [aresta=12; area=144; hipotenusa=16.97056274847714]
	 maior que o quadrado [aresta=1; area=1; hipotenusa=1.4142135623730951]
O quadrado [aresta=12; area=144; hipotenusa=16.97056274847714] é 
	 maior que o quadrado [aresta=1; area=1; hipotenusa=1.4142135623730951]
	 menor que o quadrado [aresta=35; area=1225; hipotenusa=49.49747468305833]
	 igual ao quadrado [aresta=12; area=144; hipotenusa=16.97056274847714]
	 menor que o quadrado [aresta=35; area=1225; hipotenusa=49.49747468305833]
	 igual ao quadrado [aresta=12; area=144; hipotenusa=16.97056274847714]
	 maior que o quadrado [aresta=1; area=1; hipotenusa=1.4142135623730951]
O quadrado [aresta=1; area=1; hipotenusa=1.4142135623730951] é 
	 igual ao quadrado [aresta=1; area=1; hipotenusa=1.4142135623730951]
	 menor que o quadrado [aresta=35; area=1225; hipotenusa=49.49747468305833]
	 menor que o quadrado [aresta=12; area=144; hipotenusa=16.97056274847714]
	 menor que o quadrado [aresta=35; area=1225; hipotenusa=49.49747468305833]
	 menor que o quadrado [aresta=12; area=144; hipotenusa=16.97056274847714]
	 igual ao quadrado [aresta=1; area=1; hipotenusa=1.4142135623730951]

Perceba que neste caso, a comparação entre dois objetos Quadrado é feita fora da classe Quadrado. Esta comparação que fizemos não é uma comparação natural de um objeto Quadrado, ou seja, um objeto do tipo Quadrado não sabe olhar para outro Quadrado e dizer se ele próprio é maior, menor ou igual ao Quadrado para o qual ele está olhando
Isso é meio chato, por que não teremos nunca como garantir um critério padrão para comparar objetos Quadrado. Digo isso pelo seguinte: Os critérios que utilizamos acima, fomos nós que definimos. Nada impede de um Joselito maluco da vida estabelecer um outro algoritmo, com um critério totalmente diferente do nosso para estipular quem é maior, menor ou igual a quem. O legal mesmo seria a própria classe Quadrado estabelecer o critério de comparação entre seus objetos. Nada mais justo, não é mesmo?
E de que forma podemos fazer isto? Bom, podemos seguir nossa intuiçao e colocar um método lá dentro da classe Quadrado com esse propósito. Que tal um método chamado, por exemplo, compararCom? O nome é bem intuitivo, né mesmo? Ok, mas agora vamos pensar em como esse método vai funcionar, para que possamos definir sua assinatura. A idéia doe colocarmos o método compararCom na classe Quadrado é permitir que um objeto desta classeQuadrado(q1, por exemplo) possa se auto-comparar com outro objeto do tipo [i]Quadrado/i. Isso nos leva a imaginar que queremos poder fazer algo assim com o q1:

q1.compararCom(q2)

Beleza! Creio que já temos então a assinatura do nosso método:

compararCom(Quadrado)

Mas… e que diabos esse método vai retornar??? Bom, o retorno desse método tem que ser alguma informação que nos diga de forma clara se q1 é maior, menor ou igual a q2. Que tal se o método sempre retornar um int? O valor desse int retornado seguiria a seguinte regrinha:
:arrow: Se o q1(O objeto que invoca o compararCom) for maior que q2(O objeto passado por parâmetro para o compararCom), o int retornado será um inteiro extritamente negativo(<0) qualquer.
:arrow: Se o q1 for maior que q2, o int retornado será um inteiro estritamente positivo(>0) qualquer.
:arrow: Se o q1 for igual à q2, o int retornado será o inteiro zero.
Legal! O nosso método já tem um cabeçalho completo!

public int compararCom(Quadrado)

Agora, vamos tentar adicionar esse método à nossa classe Quadrado:

01 public class Quadrado { 02 private int aresta; 03 04 public Quadrado(int aresta) { 05 if(aresta <= 0) 06 throw new IllegalArgumentException("aresta = " + aresta); 07 this.aresta = aresta; 08 } 09 10 public int getAresta() { 11 return aresta; 12 } 13 14 public long getArea() { 15 return (long)Math.pow(aresta, 2); 16 } 17 18 public double getHipotenusa() { 19 return Math.sqrt(2 * Math.pow(aresta, 2)); 20 } 21 22 public boolean equals(Object obj) { 23 if(!(obj instanceof Quadrado)) 24 return false; 25 26 Quadrado outro = (Quadrado)obj; 27 28 return compararCom(outro) == 0; 29 } 30 31 public int compararCom(Quadrado outro) { 32 return this.aresta - outro.aresta; 33 } 34 35 public String toString() { 36 return 37 "[aresta=" + aresta + 38 "; area=" + getArea() + 39 "; hipotenusa=" + getHipotenusa() + "]" 40 ; 41 } 42 }
Na linha 31 aparece nosso novo método!!! Veja como ficou simples a implementação dele: Se a aresta do objeto que invocou o compararCom(representado pelo this) for maior que a aresta do objeto passado por parâmetro(representado por outro), então isso quer dizer que o objeto que invocou é maior que o objeto passado por parâmetro. Neste caso, o método deveria retornar um inteiro positivo, e é exatamente isso que ele faz! Como a aresta do this é maior que a aresta do outro, a primeira menos a segunda dá um valor positivo. E assim por diante.
Note também que alteramos o método equals, mais precisamente a linha 33. Estamos atrelando o resultado do equals com o resultado do compararCom. Fazemos isso porque parece ser extremamente consistente o equals retornar true quando o compararCom retornar 0, e vice-versa, uma vez que esses dois casos trazem a mesma semântica (significado).
Agora, em qualquer lugar do mundo em que nossa classe Quadrado for utilizada, os programadores poderão utilizar o método compararCom para conseguir obter uma ordenação natural de objetos do tipo Quadrado. Basta eles utilizarem assim nossa classe:

01 public class TesteQuadrado { 02 public static void main(String[] args) { 03 Quadrado[] quadrados = { 04 new Quadrado(1), 05 new Quadrado(35), 06 new Quadrado(12), 07 new Quadrado(35), 08 new Quadrado(12), 09 new Quadrado(1), 10 }; 11 12 for(int quad = 0; quad < quadrados.length; quad++) { 13 System.out.println("O quadrado " + quadrados[quad] + " é "); 14 for (int cmp = 0; cmp < quadrados.length; cmp++) { 15 String comparacao; 16 if(quadrados[quad].compararCom(quadrados[cmp]) > 0) 17 comparacao = " maior que "; 18 else 19 if(quadrados[quad].compararCom(quadrados[cmp]) < 0) 20 comparacao = " menor que "; 21 else 22 comparacao = " igual a"; 23 24 System.out.println( 25 "\t" + comparacao + "o quadrado " + quadrados[cmp] 26 ); 27 } 28 } 29 } 30 }
Basicamente, o que mudou foram as linhas 16 e 19. Ao invez de compararmos os resultados dos getAresta de cada objeto, utilizamos o resultado do compararCom para determinarmos se quadrados[quad] é maior, menor ou igual a quadrados[cmp]. Pouca coisa de vantagem? parece que sim, mas não é bem por aí… Parece que a vantagem é pequena porque o critério de comparação utilizado por Quadrado é relativamente simples. Imagine se tivessemos uma classe cujo critério de comparação dependesse de… hmm… 5 de seus 10 campos… :twisted: :shock: :shock: :shock: :twisted:
Outro ponto a favor, é que o critério em si, bem como sua implementação, ficam encapsulados dentro da classe, transparentes a quem vai utilizar a classe Quadrado.
Ok! Nossa classe agora tem um mecanismo próprio para estabelecer uma ordenação natural entre seus objetos. Legal! Mas se você acha que acabamos de inventar uma coisa nova, está redondamente enganado… Esse conceito de classes que definel uma ordenação natural para seus objetos vem deeeesde o Java 1.2. Tanto é, que existem classes da API Java que tiram proveito de objetos cujas classes determinam uma ordenação natural. Algumas classes e interfaces da API Java utilizam objetos desse tipo para criar conjuntos ordenados. Há também classes que têm métodos próprios para ordenar arrays e listas de objetos com base na ordenação natural destes.
Mas cabe aqui uma pergunta: Se toda essa infra-estrutura existe desde o Java 1.2 (bem velhinho…), como é que essas classes do Java mencionadas acima, que são mais velhas que a nossa classe Quadrado, vão advinhar que na nossa classe o método que faz a comparação chama-se compararCom, recebe um parâmetro do tipo Quadrado e retorna um int??? Simples: Elas não vão advinhar nada:frowning:
Mas então, como podemos “conectar” nossa classe Quadrado com essas classes do Java?
Estas classes da API Java que utilizam objetos auto-comparáveis estabelecem uma regrinha para a classe dos objetos que elas manipulam: A classe deve implementar a interface [i]Comparable/i. Vamos pegar um exemplo: A classe [i]TreeSet/i implementa um conjunto de objetos que estão sempre ordenados. É como se fosse uma lista de objetos, que sempre está ordenada, e não tem objetos repetidos(objetos iguais). Sempre que você adiciona um objeto dentro de um TreeSet, esse TreeSet vai verificar se a classe do objeto que você inseriu determina uma ordenação natural. De que forma ele faz isso? Simples: verficando se a classe deste objeto dá um implements da interface Comparable! Veja o seguinte exemplo:

01 import java.util.TreeSet;
02 
03 public class TesteTreeSet01 {
04     public static void main(String[] args) {
05         /*
06          * Criamos uma instância de TreeSet que vai armazenar objetos do tipo
07          * Quadrado
08          */
09         TreeSet<Quadrado> setQuadrados = new TreeSet<Quadrado>();
10         /*
11          * Criamos um vetor de tipo Quadrado com um punhado de objetos Quadrado
12          * fora de ordem
13          */
14         Quadrado[] quadrados = {
15             new Quadrado(1),
16             new Quadrado(35),
17             new Quadrado(12),
18             new Quadrado(35),
19             new Quadrado(12),
20             new Quadrado(1),
21         };
22         
23         System.out.println("Quadrados desordenados:");
24         for(Quadrado quadrado: quadrados) {
25             System.out.println("\t" + quadrado);
26         }
27         System.out.println();
28         
29         /*
30          * Adicionamos ao TreeSet setQuadrados, um a um, os objetos Quadrado 
31          * contidos no vetor quadrados. O método add da classe TreeSet, insere 
32          * o objeto passado já na ordem certa. 
33          */
34         for (int q = 0; q < quadrados.length; q++) {
35             setQuadrados.add(quadrados[q]);
36         }
37         
38         System.out.println("Quadrados ordenados:");
39         for(Quadrado quadrado: setQuadrados) {
40             System.out.println("\t" + quadrado);
41         }
42     }
43 }

Neste código, estamos criando uma instância de TreeSet, que armazenará ordenadamente os elementos do vetor de Quadrado quadrados. Intuitivamente, bastaria adicionar os objetos Quadrado em setQuadrados e pronto! Teríamos um conjunto de quadrados ordenado e sem repetições. Porém, há um pequeno problema: A classe TreeSet não tem como advinhar que a classe Quadrado estabelece uma ordenação natural… A classe Quadrado teria que “dar uma dica” disso pra classe TreeSet… Se executarmos o código acima, teremos uma saída mais ou menos assim:

Quadrados desordenados:
	[aresta=1; area=1; hipotenusa=1.4142135623730951]
	[aresta=35; area=1225; hipotenusa=49.49747468305833]
	[aresta=12; area=144; hipotenusa=16.97056274847714]
	[aresta=35; area=1225; hipotenusa=49.49747468305833]
	[aresta=12; area=144; hipotenusa=16.97056274847714]
	[aresta=1; area=1; hipotenusa=1.4142135623730951]

Exception in thread "main" java.lang.ClassCastException: Quadrado
	at java.util.TreeMap.compare(TreeMap.java:1093)
	at java.util.TreeMap.put(TreeMap.java:465)
	at java.util.TreeSet.add(TreeSet.java:210)
	at TesteTreeSet01.main(TesteTreeSet01.java:40)

Uma exceção lançada pela classe TreeSet!!! Mas por quê? Porque como a classe TreeSet esperava que a classe do objeto inserido (Quadrado) implementasse a interface Comparable! A classe TreeSet confiava que a classe Quadrado implementasse a interface Comparable. Mas nós traímos tal confiança… Em resposta, somos punidos com o lançamento de uma ClassCastException. Isso acontece porque, internamente, ao ser inserido um elemento (que não o primeiro do TreeSet), o TreeSet vai querer comparar o elemento sendo inserido com os elementos já existentes na lista. Pra isso, ele vai tentar fazer um cast mais ou menos assim:

Comparable objComparavelSendoInserido = 
   (Comparable)quadradoSendoInserido;

Notou como a classe TreeSet “confia” que o objeto inserido é de uma classe que implementa Comparable? Ela vai lá e faz o cast direto. Bom, como a nossa classe Quadrado não implementa Comparable, a instrução acima conhecidamente lança uma ClassCastException.
Bom, agora sabemos que as classes da API java que utilizam algum mecanismo de comparação de outros objetos, geralmente exigem que esses objetos sejam objetos Comparable.
Mas aí cabe uma pergunta: De que adianta a nossa classe implementar Comparable? Por acaso isso vai fazer alguma mágica capaz de dar poderes de advinhação ao, por exemplo, TreeSet, permitindo que ele “descubra” qual é o método que faz a comparação?
Resposta: Quase isso… Na verdade, a interface Comparable declara um método com a seguinte assinatura:

public int compareTo(T)

Familiar essa assinatura, não? :twisted:
O contrato (A grosso modo: regras de uso. Procurem ler a respeito de “design by contract”) do método compareTo, em linhas gerais, estabelece que a implementação deste método deve estabelecer uma ordenação aos objetos da classe que dá implements em Comparable. Partindo do pressuposto que uma classe que implementa uma interface, obrigatoriamente implementa seus métodos, e partindo também do pressuposto que o contrato dos métodos implementados foi “honrado”, um TreeSet faz algo parecido com isso:

for(T elementoJaInserido: todosElementosInseridos){
   Comparable comparavelSendoInserido = (Comparable) elementoSendoInserido;
   int resultadoComparacao = comparavelSendoInserido.compareTo(elementoJaInserido);
   //continua...
}

É dessa forma que o TreeSet faz a comparação: Como ele assume que os elementos são objetos de classes que dão implements em Comparable, nada mais justo de admitir, por conseqüência, que estes objetos podem ser convertido para Comparable e utilizar então o método compareTo desta interface.
Essa é a “dica” que a classe Quadrado deve dar para que, não só as classes da API Java mencionadas antes, mas também para todos nós programadores, de que seus objetos têm uma ordenação natural: implements Comparable!!!
Façamos a seguinte alteração na classe Quadrado:

01 public class Quadrado implements Comparable<Quadrado>{ 02 private int aresta; 03 04 public Quadrado(int aresta) { 05 if(aresta <= 0) 06 throw new IllegalArgumentException("aresta = " + aresta); 07 this.aresta = aresta; 08 } 09 10 public int getAresta() { 11 return aresta; 12 } 13 14 public long getArea() { 15 return (long)Math.pow(aresta, 2); 16 } 17 18 public double getHipotenusa() { 19 return Math.sqrt(2 * Math.pow(aresta, 2)); 20 } 21 22 public boolean equals(Object obj) { 23 if(!(obj instanceof Quadrado)) 24 return false; 25 26 Quadrado outro = (Quadrado)obj; 27 28 return compareTo(outro) == 0; 29 } 30 31 public int compareTo(Quadrado outro) { 32 return this.aresta - outro.aresta; 33 } 34 35 public String toString() { 36 return 37 "[aresta=" + aresta + 38 "; area=" + getArea() + 39 "; hipotenusa=" + getHipotenusa() + "]" 40 ; 41 } 42 }
Alteramos agora a linha 1 para declarar que a nossa classe implementa a interface Comparable. Na linha 31, trocamos nosso método compararCom por uma implementação do método compareTo, previsto na interface Comparable (Na verdade trocamos apenas os nomes dos métodos… :lol: ).
Agora sim, se rodarmos novamente aquela classe TesteTreeSet, vai funcionar direitinho, dando a seguinte saída:

Quadrados desordenados:
	[aresta=1; area=1; hipotenusa=1.4142135623730951]
	[aresta=35; area=1225; hipotenusa=49.49747468305833]
	[aresta=12; area=144; hipotenusa=16.97056274847714]
	[aresta=35; area=1225; hipotenusa=49.49747468305833]
	[aresta=12; area=144; hipotenusa=16.97056274847714]
	[aresta=1; area=1; hipotenusa=1.4142135623730951]

Quadrados ordenados:
	[aresta=1; area=1; hipotenusa=1.4142135623730951]
	[aresta=12; area=144; hipotenusa=16.97056274847714]
	[aresta=35; area=1225; hipotenusa=49.49747468305833]

Temos aí a listagem dos punhado de quadrados que tinhamos, e depois uma listagem do conjunto ordenado de quadrados, implementado pelo TreeSet.
Um outro exemplo de classe do Java que utiliza objetos Comparable, é a classe [i]Arrays/i. Ela é uma classe utilitária que tem vários métodos estáticos para auxiliar a manipulação de vetores. Um destes métodos é o sort, utilizado para ordenar um vetor. Uma das suas várias assinaturas é esta:

public static void sort(Object[])

Esta sobrecarga de sort exige que os elementos do vetor passado por parâmetro seja de uma classe que implemente a interface Comparable, senão ele vai dar o mesmo problema que vimos com o TreeSet
Rodem o seguinte programa:

01 import java.util.Arrays; 02 03 public class TesteArrays { 04 public static void main(String[] args) { 05 /* 06 * Criamos um vetor de tipo Quadrado com um punhado de objetos Quadrado 07 * fora de ordem 08 */ 09 Quadrado[] quadrados = { 10 new Quadrado(1), 11 new Quadrado(35), 12 new Quadrado(12), 13 new Quadrado(35), 14 new Quadrado(12), 15 new Quadrado(1), 16 }; 17 18 System.out.println("Quadrados desordenados:"); 19 for(Quadrado quadrado: quadrados) { 20 System.out.println("\t" + quadrado); 21 } 22 System.out.println(); 23 24 /* 25 * Utilizamos o método sort de Arrays para ordenar o nosso vetor 26 * quadrados de acordo com a ordenação natural definida pela classe 27 * Quadrado 28 */ 29 Arrays.sort(quadrados); 30 31 System.out.println("Quadrados ordenados:"); 32 for(Quadrado quadrado: quadrados) { 33 System.out.println("\t" + quadrado); 34 } 35 } 36 }
O resultado esperedo é o seguinte:

Quadrados desordenados:
	[aresta=1; area=1; hipotenusa=1.4142135623730951]
	[aresta=35; area=1225; hipotenusa=49.49747468305833]
	[aresta=12; area=144; hipotenusa=16.97056274847714]
	[aresta=35; area=1225; hipotenusa=49.49747468305833]
	[aresta=12; area=144; hipotenusa=16.97056274847714]
	[aresta=1; area=1; hipotenusa=1.4142135623730951]

Quadrados ordenados:
	[aresta=1; area=1; hipotenusa=1.4142135623730951]
	[aresta=1; area=1; hipotenusa=1.4142135623730951]
	[aresta=12; area=144; hipotenusa=16.97056274847714]
	[aresta=12; area=144; hipotenusa=16.97056274847714]
	[aresta=35; area=1225; hipotenusa=49.49747468305833]
	[aresta=35; area=1225; hipotenusa=49.49747468305833]

Veja que o vetor quadrados foi alterado de forma a ter todos seus elementos postos em ordem, segundo os critérios implementados pelo método compareTo da classe Quadrado.
Mais uma coisa a se notar: Percebeu que as vezes chamamos um “objeto de classe a qual implementa Comparable” simplesmente de “objeto Comparable”? Isto traz uma carga semântica muito importante! “Comparable” é igual a “comparável” em inglês. Então, um objeto de uma classe a qual implementa a interface Comparable, é um objeto que pode ser comparado.
[size=18]Já a interface Comparator[/size], tem também o propósito de estabelecer a ordenação entre objetos, mas de uma forma um pouco diferente…
Imagine a seguinte classe:

01 public class Cliente implements Comparable<Cliente>{ 02 private String 03 nome, 04 cpf 05 ; 06 private char sexo; 07 private double 08 peso, 09 altura 10 ; 11 12 //Getters e Setters omitidos 13 14 public boolean equals(Object obj) { 15 if(!(obj instanceof Cliente)) 16 return false; 17 18 Cliente outro = (Cliente)obj; 19 20 return this.compareTo(outro) == 0; 21 } 22 23 public String toString() { 24 return 25 "[cpf=" + cpf + 26 "; nome=" + nome + 27 "; sexo=" + sexo + 28 "; peso=" + peso + 29 "; altura=" + altura + "]" 30 ; 31 } 32 33 public int compareTo(Cliente outro) { 34 if(outro == null) 35 throw new ClassCastException("null"); 36 37 return this.cpf.compareTo(outro.cpf); 38 } 39 }
Ok! Até aqui, nada de novo. Mas e se quisermos estabelecer um outro critério de ordenação para os objetos Cliente? Por exemplo, como faríamos para ter um vetor de objetos Cliente, ordenado não pelo cpf, mas pelo nome?
Inicialmente, podemos simplesmente, no meio do nosso código, implementar algum algoritimo de ordenação e ordenar o vetor utilizando o método getNome dos objetos no vetor. O primeiro ponto ruim disso é que vai melecar o seu código.
Mas aí podemos pensar em criar uma classe só pra fazer tal ordenação. Legal! Mas vamos ter que continuar a implementar algum algoritmo de ordenação… Estariamos re-inventando a roda, por que o Java já tem na classe Arrays métodos para ordenação de vetores. Mas como que o Arrays vai saber que não queremos utilizar o critério da ordenação natural (ordenar por cpf), e sim, um outro critério (ordenação por nome)? É aí que entra em jogo a interface [i]Comparator/i!
Olhe só que interessante essa sobrecarga do método sort da classe Arrays:

public static <T> void sort(T[] a, Comparator<? super T> c)

Temos aí agora como parâmetro, além do vetor a ser ordenado, um objeto de uma classe a qual implemente a interface Comparator(um objeto Comparator). Qual é o papel da interface Comparator nesse método?
A interface Comparator declara dois métodos:
:arrow: public int compare(T, T)
:arrow: public boolean equals(Object)
Para este post, esqueça esse método equals da interface. Apenas tenha em mente que este equals não diz respeito aos objetos que comparados. Este equals é para verificar se dois objetos Comparator são equivalentes. Trata-se de um uso avançado, que não abordaremos aqui.
O contrato do método compare é semelhante ao do compareTo da interface Comparable. A diferença é que, enquanto o compareTo serve para comparar o próprio objeto que invoca o compareTo (this) com o objeto passado por parâmetro (outro), o método compare da interface Comparable serve para comparar o objeto do tipo T passado no primiero parâmetro com o objeto do tipo T passado no segundo parâmetro.
A classe que implementar a interface Comparator, terá que implementar o método compare, implementando nele algum critério de comparação entre objetos do tipo T, seja lá quem for T.
Isso é interessante para nós, pois temos aqui a chance de criar o tal vetor de clientes ordenado por nome. Podemos criar uma classe que implemente Comparator e escrever o método compare de tal forma, que estabeleça que
:arrow: Um objeto Cliente c1 é menor que um objeto Cliente c2, se o nome de c1 for “alfabeticamente menor” que o nome de c2. Neste caso, compare deve retornar um número estritamente negativo.
:arrow: Um objeto Cliente c1 é maior que um objeto Cliente c2, se o nome de c1 for “alfabeticamente maior” que o nome de c2. Neste caso, compare deve retornar um número estritamente positivo.
:arrow: Um objeto Cliente c1 é igual a um objeto Cliente c2, se o nome de c1 for “alfabeticamente igual” ao nome de c2. Neste caso, compare deve retornar zero.

Vamos ver como ficaria esse Comparator:

01 import java.util.Comparator; 02 03 public class ClienteNomeComparator implements Comparator<Cliente> { 04 public int compare(Cliente c1, Cliente c2) { 05 if(c1 == null || c2 == null) 06 throw new ClassCastException("null"); 07 08 String 09 nome1 = c1.getNome(), 10 nome2 = c2.getNome() 11 ; 12 13 /* 14 * Aproveitamo-nos do fato de a classe String implementar Comparable, 15 * evitando que façamos todo o trabalho duro para verificar se um String 16 * é alfabéticamente maior ou menor que outro. 17 */ 18 return nome1.compareTo(nome2); 19 } 20 }
Agora, vamos criar uma classe de teste que vai criar um vetor de clientes e imprimila 3 vezes: desordenada, ordenada por nome e ordenada naturalmente (por cpf):

01 import java.util.Arrays; 02 03 public class TesteClienteArrays { 04 public static void main(String[] args) { 05 Cliente[] clientes = { 06 new Cliente("Antonin Dvorak", "745.333.560-44", 'M', 67.38, 1.65), 07 new Cliente("Piotr Yilitch Tchaikowiski", "764.099.230-65", 'M', 53.22, 1.90), 08 new Cliente("Joao da Silva", "000.000.001-99", 'M', 75.16, 1.75), 09 new Cliente("Gustav Mahler", "000.000.001-99", 'M', 51.27, 1.87), 10 new Cliente("Joao da Silva", "000.000.002-75", 'M', 66.16, 1.64), 11 }; 12 13 System.out.println("Lista desordenada de clientes:"); 14 for (int c = 0; c < clientes.length; c++) { 15 System.out.println("\t" + clientes[c]); 16 } 17 System.out.println(); 18 19 /* 20 * Utilizando sort(T, Comparator<? super T>) 21 */ 22 ClienteNomeComparator comparadorPorNome = new ClienteNomeComparator(); 23 Arrays.sort(clientes, comparadorPorNome); 24 25 System.out.println("Lista de clientes por nome:"); 26 for (int c = 0; c < clientes.length; c++) { 27 System.out.println("\t" + clientes[c]); 28 } 29 System.out.println(); 30 31 /* 32 * Utilizando sort(Object[]) 33 */ 34 Arrays.sort(clientes); 35 36 System.out.println("Lista de clientes por cpf(natural):"); 37 for (int c = 0; c < clientes.length; c++) { 38 System.out.println("\t" + clientes[c]); 39 } 40 } 41 }
Na linha 23, estamos ordenando o vetor clientes com o sort que usa um Comparator para realizar a ordenação. Internamente, sempre que ele precisar comparar alguma elemento e1 com algum outro elemento e2 do vetor, ele vai fazer algo parecido com isto:

int resultadoComparacao =
   objetoComparatorPassadoPorParametro.compare(e1, e2);
//Faz alguma coisa com esse resultado

A saída deste programa deve ser a seguinte:

Lista desordenada de clientes:
	[cpf=745.333.560-44; nome=Antonin Dvorak; sexo=M; peso=67.38; altura=1.65]
	[cpf=764.099.230-65; nome=Piotr Yilitch Tchaikowiski; sexo=M; peso=53.22; altura=1.9]
	[cpf=000.000.001-99; nome=Joao da Silva; sexo=M; peso=75.16; altura=1.75]
	[cpf=000.000.001-99; nome=Gustav Mahler; sexo=M; peso=51.27; altura=1.87]
	[cpf=000.000.002-75; nome=Joao da Silva; sexo=M; peso=66.16; altura=1.64]

Lista de clientes por nome:
	[cpf=745.333.560-44; nome=Antonin Dvorak; sexo=M; peso=67.38; altura=1.65]
	[cpf=000.000.001-99; nome=Gustav Mahler; sexo=M; peso=51.27; altura=1.87]
	[cpf=000.000.001-99; nome=Joao da Silva; sexo=M; peso=75.16; altura=1.75]
	[cpf=000.000.002-75; nome=Joao da Silva; sexo=M; peso=66.16; altura=1.64]
	[cpf=764.099.230-65; nome=Piotr Yilitch Tchaikowiski; sexo=M; peso=53.22; altura=1.9]

Lista de clientes por cpf(natural):
	[cpf=000.000.001-99; nome=Gustav Mahler; sexo=M; peso=51.27; altura=1.87]
	[cpf=000.000.001-99; nome=Joao da Silva; sexo=M; peso=75.16; altura=1.75]
	[cpf=000.000.002-75; nome=Joao da Silva; sexo=M; peso=66.16; altura=1.64]
	[cpf=745.333.560-44; nome=Antonin Dvorak; sexo=M; peso=67.38; altura=1.65]
	[cpf=764.099.230-65; nome=Piotr Yilitch Tchaikowiski; sexo=M; peso=53.22; altura=1.9]

Um outro uso é criar uma classe que implemente Comparator para estabelecer uma ordenação entre objetos de uma classe a qual não define uma ordenação natural (não implementa Comparable).
A classe TreeSet tem também um contrutor que recebe um objeto Comparator por parâmetro. É uma boa saída para criar conjuntos do objetos não-comparáveis naturalmente. Só fique atento para o seguinte: Nesses casos, nem sempre é fácil ou mesmo possível manter uma consistência entre os métodos equals e o compare. Você deve analisar qual o possível impacto que isso possa ter no seu programa.

Bom, Acho que é isso.
Qualquer dúvida ou correção, postem mais.
Valeu!

Mantu, seus tópicos dariam um livro da mais alta qualidade! :smiley:
Pq vc não pega alguns deles e transforma em tutoriais aqui para o GUJ? :wink:

Isso é que é qualidade GUJ de resposta!

Como o Ironlynx disse Mantu, seus tópicos dariam tutoriais excelentes !

Continue assim hehe… pq tah me ajudando pra karamba… hehe !! !! !!

[quote=Ironlynx]Mantu, seus tópicos dariam um livro da mais alta qualidade! :smiley:
Pq vc não pega alguns deles e transforma em tutoriais aqui para o GUJ? :wink:[/quote]
Eu já enviei um uma vez, mas não tive resposta. Talvez tenha ficado aquém do esperado de um tutoria do guj, hehehe…
Obrigado pela força, pessoal! :thumbup:

Bom dia… Porque que quando eu chamo:

[code]
import java.util.*;

public class Amigo implements Comparator {

public int compare(Pessoa p, Pessoa pp) {
	return p.getIdade().compareTo(pp.getIdade());
}

}[/code]

o metodo getIdade() é da instancia int idade;
Ele diz esse erro:

.\Amigo.java:6: int cannot be dereferenced
		return p.getIdade().compareTo(pp.getIdade());
                                 ^

um tipo int nao pode ser derreferenciado?