JavaRush /Blogue Java /Random-PT /Comparador em Java

Comparador em Java

Publicado no grupo Random-PT
Olá! Hoje falaremos sobre comparação de objetos. Hmm... Mas parece que já conversamos sobre isso mais de uma vez? :/ Sabemos como ==funciona o operador “ ”, assim como os métodos equals()e hashCode(). Comparação não é realmente sobre isso. Anteriormente, queríamos dizer mais como “testar objetos quanto à igualdade”. Comparador em Java - 1Mas comparar objetos entre si pode ter objetivos completamente diferentes! O mais óbvio é a classificação. Acho que se lhe disserem para classificar uma lista de ArrayList<>números ou strings, você poderá lidar com isso sem problemas:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       String name1 = "Masha";
       String name2 = "Sasha";
       String name3 = "Даша";

       List<String> names = new ArrayList<>();
       names.add(name1);
       names.add(name2);
       names.add(name3);

       Collections.sort(names);
       System.out.println(names);
   }
}
Saída do console:

[Даша, Маша, Саша]
Seria ótimo se você se lembrasse da classe Collectionse de seu método sort(). Também não creio que haverá problemas com números. Aqui está uma tarefa mais difícil para você:
public class Car {

   private int manufactureYear;
   private String model;
   private int maxSpeed;

   public Car(int manufactureYear, String model, int maxSpeed) {
       this.manufactureYear = manufactureYear;
       this.model = model;
       this.maxSpeed = maxSpeed;
   }

   //...геттеры, сеттеры, toString()

}

import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<Car> cars = new ArrayList<>();

       Car ferrari = new Car(1990, "Ferrari 360 Spider", 310);
       Car lambo = new Car(2012, "Lamborghini Gallardo", 290);
       Car bugatti = new Car(2010, "Bugatti Veyron", 350);

       cars.add(ferrari);
       cars.add(bugatti);
       cars.add(lambo);
   }
}
É muito simples: uma classe Care 3 de seus objetos. Tenha a gentileza de classificar os carros da lista! Você provavelmente perguntará: “Como eles devem ser classificados?” Por nome, por ano de fabricação, por velocidade máxima? Ótima pergunta. Não sabemos no momento como ordenar os objetos da classe Car. E, naturalmente, Java também não sabe disso! Quando tentamos passar Collections.sort()uma lista de objetos para um método Car, receberemos um erro:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<Car> cars = new ArrayList<>();

       Car ferrari = new Car(1990, "Ferrari 360 Spider", 310);
       Car lambo = new Car(20012, "Lamborghini Gallardo", 290);
       Car bugatti = new Car(2010, "Bugatti Veyron", 350);

       cars.add(ferrari);
       cars.add(bugatti);
       cars.add(lambo);

       //ошибка компилятора!
       Collections.sort(cars);
   }
}
E realmente, como a linguagem sabe exatamente como classificar os objetos que você escreve? Depende dos objetivos do seu programa. Temos que de alguma forma ensinar Java a comparar esses objetos. E compare a maneira como precisamos. Para isso, Java possui uma interface de ferramenta especial Comparable. Em inglês isso é traduzido como “comparável”. Para que nossos objetos Carsejam comparados entre si e de alguma forma classificados, a classe deve implementar esta interface e implementar seu único método compareTo():
public class Car implements Comparable<Car> {

   private int manufactureYear;
   private String model;
   private int maxSpeed;

   public Car(int manufactureYear, String model, int maxSpeed) {
       this.manufactureYear = manufactureYear;
       this.model = model;
       this.maxSpeed = maxSpeed;
   }

   @Override
   public int compareTo(Car o) {
       return 0;
   }

   //...геттеры, сеттеры, toString()

}
Prestar atenção:especificamos a interface Comparable<Car>, não apenas Comparable. É uma interface digitada, o que significa que requer a especificação da classe específica à qual está associada. Em princípio, <Car>você pode removê-lo da interface, mas ele compara objetos por padrão Object. Em vez de um método compareTo(Car o)em nossa classe teremos:
@Override
   public int compareTo(Object o) {
       return 0;
   }
Claro, é muito mais fácil para nós trabalharmos com Car. Dentro do método compareTo()implementamos a lógica de comparação de máquinas. Digamos que precisamos classificá-los por ano de fabricação. Você provavelmente notou que o método compareTo()retorna o valor int, não boolean. Não deixe isso te surpreender. O fato é que comparar dois objetos nos dá 3 opções possíveis:
  • а < b
  • a > b
  • a == b.
Possui booleanapenas 2 valores - verdadeiro e falso, o que é inconveniente para comparar objetos. Tudo é intmuito mais simples. Se o valor de retorno > 0for , então a > b. Se o resultado compareTo < 0for , então а < b. Bem, se o resultado for == 0, então os dois objetos são iguais: a == b. Ensinar nossa turma a classificar os carros por ano de fabricação é tão fácil quanto descascar peras:
@Override
public int compareTo(Car o) {
   return this.getManufactureYear() - o.getManufactureYear();
}
O que está acontecendo aqui? Pegamos um objeto carro ( this), o ano de fabricação desse carro, e subtraímos dele o ano de fabricação de outro carro (aquele com o qual comparamos o objeto). Se o ano de fabricação do primeiro carro for maior, o método retornará int > 0. O que significa que um carro this >é um carro о. Se, pelo contrário, o ano de fabricação do segundo carro ( о) for maior, então o método retornará um número negativo e, portanto о > this, . Bem, se forem iguais, o método retornará 0. Um mecanismo tão simples já é suficiente para classificar coleções de objetos Car! Você não precisa fazer mais nada. Olha Você aqui:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<Car> cars = new ArrayList<>();

       Car ferrari = new Car(1990, "Ferrari 360 Spider", 310);
       Car lambo = new Car(2012, "Lamborghini Gallardo", 290);
       Car bugatti = new Car(2010, "Bugatti Veyron", 350);

       cars.add(ferrari);
       cars.add(bugatti);
       cars.add(lambo);

       //тут раньше была ошибка
       Collections.sort(cars);
       System.out.println(cars);
   }
}
Saída do console:

[Car{manufactureYear=1990, model='Ferrari 360 Spider', maxSpeed=310}, 
Car{manufactureYear=2010, model='Bugatti Veyron', maxSpeed=350}, 
Car{manufactureYear=2012, model='Lamborghini Gallardo', maxSpeed=290}]
Os carros são classificados como deveriam ser! :) Comparador em Java - 2Em que casos deve ser usado Comparable? O método de comparação implementado Comparableé denominado “ordenação natural”. Isso ocorre porque no método compareTo()você descreve o método de comparação mais comum que será utilizado para objetos desta classe em seu programa. A ordenação natural já está presente em Java. Por exemplo, Java sabe que as strings são geralmente classificadas em ordem alfabética e os números são geralmente classificados por valor crescente. Portanto, se você chamar o método em uma lista de números ou strings sort(), eles serão ordenados. Se em nosso programa os carros serão, na maioria dos casos, comparados e classificados por ano de fabricação, então vale a pena definir uma classificação natural para eles usando a interface Comparable<Car>e o método compareTo(). Mas e se isso não for suficiente para nós? Vamos imaginar que nosso programa não seja tão simples. Na maioria dos casos, a classificação natural dos carros (definimos por ano de fabricação) nos convém. Mas às vezes há fãs de direção rápida entre nossos clientes. Se estamos preparando um catálogo de carros para eles escolherem, eles precisam ser ordenados pela velocidade máxima. Comparador em Java - 3Por exemplo, precisamos dessa classificação em 15% dos casos. Isto claramente não é suficiente para definir a classificação natural por Carvelocidade em vez de ano de fabricação. Mas não podemos ignorar 15% dos clientes. O que nós fazemos? Aqui outra interface vem em nosso auxílio - Comparator. Assim como , Comparableé digitado. Qual é a diferença? Comparabletorna nossos objetos "comparáveis" e cria a ordem de classificação mais natural para eles, que será usada na maioria dos casos. Comparator- esta é uma classe “comparadora” separada (a tradução é um pouco desajeitada, mas compreensível). Se precisarmos implementar alguma ordenação específica, não precisamos entrar na classe Care alterar a lógica compareTo(). Em vez disso, podemos criar uma classe comparadora separada em nosso programa e ensiná-la a fazer a classificação necessária!
import java.util.Comparator;

public class MaxSpeedCarComparator implements Comparator<Car> {

   @Override
   public int compare(Car o1, Car o2) {
       return o1.getMaxSpeed() - o2.getMaxSpeed();
   }
}
Como você pode ver, o nosso Comparatoré bastante simples. Existe apenas um método compare()- este é um método de interface Comparatorque deve ser implementado. Ele pega dois objetos como entrada Care compara sua velocidade máxima da maneira usual (por subtração). Assim como compareTo(), ele retorna o número int, o princípio de comparação é o mesmo. Como podemos usar isso? Muito simples:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<Car> cars = new ArrayList<>();

       Car ferrari = new Car(1990, "Ferrari 360 Spider", 310);
       Car lambo = new Car(2012, "Lamborghini Gallardo", 290);
       Car bugatti = new Car(2010, "Bugatti Veyron", 350);

       cars.add(ferrari);
       cars.add(bugatti);
       cars.add(lambo);

       Comparator speedComparator = new MaxSpeedCarComparator();
       Collections.sort(cars, speedComparator);

       System.out.println(cars);
   }
}
Saída do console:

[Car{manufactureYear=2012, model='Lamborghini Gallardo', maxSpeed=290}, 
Car{manufactureYear=1990, model='Ferrari 360 Spider', maxSpeed=310}, 
Car{manufactureYear=2010, model='Bugatti Veyron', maxSpeed=350}]
Simplesmente criamos um objeto comparador e o passamos para o método Collections.sort()junto com a lista a ser classificada. Tendo recebido um comparador como entrada, o método sort()não utilizará a ordenação natural definida na compareTo()classe method Car. Em vez disso, aplicará o algoritmo de classificação do comparador passado a ele. Que vantagens isso nos traz? Em primeiro lugar, compatibilidade com o código escrito. Criamos um novo método de classificação específico, mantendo o atual, que será utilizado na maioria dos casos. Não tocamos na aula de forma alguma Car. Ele Comparablepermaneceu como estava:
public class Car implements Comparable<Car> {

   private int manufactureYear;
   private String model;
   private int maxSpeed;

   public Car(int manufactureYear, String model, int maxSpeed) {
       this.manufactureYear = manufactureYear;
       this.model = model;
       this.maxSpeed = maxSpeed;
   }

   @Override
   public int compareTo(Car o) {
       return this.getManufactureYear() - o.getManufactureYear();
   }

   //...геттеры, сеттеры, toString()

}
Em segundo lugar, flexibilidade. Podemos adicionar quantos tipos quisermos. Digamos, classifique os carros por cor, velocidade, peso ou quantas vezes o carro foi usado nos filmes do Batman. Basta criar um adicional Comparator. Isso é tudo! Hoje você aprendeu dois mecanismos muito importantes que usará com frequência em projetos reais de trabalho. Mas, como você sabe, teoria sem prática não é nada. Portanto, é hora de consolidar seus conhecimentos e solucionar diversos problemas! :)
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION