JavaRush /Blog Java /Random-ES /Comparador en Java

Comparador en Java

Publicado en el grupo Random-ES
¡Hola! Hoy hablaremos de comparar objetos. Hmm... ¿Pero parece que ya hemos hablado de esto más de una vez? :/ Sabemos cómo ==funciona el operador “ ”, así como los métodos equals()y hashCode(). La comparación no se trata realmente de eso. Anteriormente, nos referíamos más a "probar la igualdad de los objetos". Comparador en Java - 1¡Pero comparar objetos entre sí puede tener objetivos completamente diferentes! El más obvio es el de clasificación. Creo que si te dicen que ordenes una lista de ArrayList<>números o cadenas, podrás manejarlo sin 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);
   }
}
Salida de consola:

[Даша, Маша, Саша]
Sería genial si recordaras la clase Collectionsy su método sort(). Tampoco creo que haya problemas con los números. Aquí tienes una tarea más difícil:
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);
   }
}
Es muy simple: una clase Cary 3 de sus objetos. ¡Sea tan amable de ordenar los coches de la lista! Probablemente te preguntarás: “¿Cómo se deben clasificar?” ¿Por nombre, por año de fabricación, por velocidad máxima? Gran pregunta. No sabemos por el momento cómo ordenar los objetos de la clase Car. Y, naturalmente, ¡Java tampoco lo sabe! Cuando intentamos pasar Collections.sort()una lista de objetos a un método Car, recibiremos un error:
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);
   }
}
Y realmente, ¿cómo sabe el lenguaje exactamente cómo ordenar los objetos que escribes? Depende de los objetivos de su programa. De alguna manera tenemos que enseñarle a Java a comparar estos objetos. Y compare la forma en que lo necesitamos. Para ello, Java dispone de una herramienta especial: la interfaz Comparable. En inglés esto se traduce como "comparable". Para que nuestros objetos Carpuedan compararse entre sí y ordenarse de alguna manera, la clase debe implementar esta interfaz e implementar su ú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 atención:especificamos la interfaz Comparable<Car>, no solo Comparable. Esta es una interfaz escrita, es decir, requiere especificar la clase específica a la que está asociada. En principio, <Car>puedes eliminarlo de la interfaz, pero luego compara objetos de forma predeterminada Object. En lugar de un método compareTo(Car o)en nuestra clase tendremos:
@Override
   public int compareTo(Object o) {
       return 0;
   }
Por supuesto, es mucho más fácil para nosotros trabajar con Car. Dentro del método compareTo()implementamos la lógica para comparar máquinas. Digamos que necesitamos ordenarlos por año de fabricación. Probablemente hayas notado que el método compareTo()devuelve el valor int, no boolean. No dejes que esto te sorprenda. El caso es que comparar dos objetos nos da 3 opciones posibles:
  • а < b
  • a > b
  • a == b.
Tiene booleansolo 2 valores: verdadero y falso, lo que resulta inconveniente para comparar objetos. Todo es intmucho más sencillo. Si el valor de retorno > 0es , entonces a > b. Si el resultado compareTo < 0es , entonces а < b. Bueno, si el resultado es == 0, entonces los dos objetos son iguales: a == b. Enseñar a nuestra clase a clasificar automóviles por año de fabricación es tan fácil como pelar peras:
@Override
public int compareTo(Car o) {
   return this.getManufactureYear() - o.getManufactureYear();
}
¿Que está pasando aqui? Tomamos un objeto de automóvil ( this), el año de fabricación de este automóvil, y le restamos el año de fabricación de otro automóvil (aquel con el que comparamos el objeto). Si el año de fabricación del primer coche es mayor, el método volverá int > 0. Lo que significa que un coche this >es un coche о. Si por el contrario el año de fabricación del segundo coche ( о) es mayor, entonces el método devolverá un número negativo, y por tanto о > this. Bueno, si son iguales, el método devolverá 0. ¡Un mecanismo tan simple ya es suficiente para clasificar colecciones de objetos Car! No necesitas hacer nada más. Aquí estás:
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);
   }
}
Salida de consola:

[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}]
¡Los coches están ordenados como deberían estar! :) ¿ Comparador en Java - 2En qué casos se debe utilizar Comparable? El método de comparación implementado Comparablese denomina "ordenamiento natural". Esto se debe a que en el método compareTo()usted describe el método de comparación más común que se utilizará para los objetos de esta clase en su programa. El orden natural ya está presente en Java. Por ejemplo, Java sabe que las cadenas se ordenan con mayor frecuencia alfabéticamente y los números se ordenan con mayor frecuencia por valor ascendente. Por lo tanto, si llama al método en una lista de números o cadenas sort(), se ordenarán. Si en nuestro programa los automóviles se comparan y clasifican en la mayoría de los casos por año de fabricación, entonces vale la pena definir una clasificación natural para ellos utilizando la interfaz Comparable<Car>y el método compareTo(). ¿Pero qué pasa si eso no es suficiente para nosotros? Imaginemos que nuestro programa no es tan sencillo. En la mayoría de los casos, nos conviene la clasificación natural de los coches (la fijamos por año de fabricación). Pero a veces entre nuestros clientes hay aficionados a la conducción rápida. Si estamos preparando un catálogo de coches para que elijan, es necesario ordenarlos por velocidad máxima. Comparador en Java - 3Por ejemplo, necesitamos dicha clasificación en el 15% de los casos. Claramente, esto no es suficiente para establecer una clasificación natural Carpor velocidad en lugar de por año de fabricación. Pero no podemos ignorar al 15% de los clientes. qué hacemos? Aquí viene en nuestra ayuda otra interfaz: Comparator. Al igual Comparableque está escrito. ¿Cual es la diferencia? Comparablehace que nuestros objetos sean "comparables" y crea el orden de clasificación más natural para ellos que se utilizará en la mayoría de los casos. Comparator- esta es una clase de "comparador" separada (la traducción es un poco torpe, pero comprensible). Si necesitamos implementar alguna clasificación específica, no tenemos que ingresar a la clase Cary cambiar la lógica compareTo(). En su lugar, podemos crear una clase de comparación separada en nuestro programa y enseñarle a realizar la clasificación que necesitamos.
import java.util.Comparator;

public class MaxSpeedCarComparator implements Comparator<Car> {

   @Override
   public int compare(Car o1, Car o2) {
       return o1.getMaxSpeed() - o2.getMaxSpeed();
   }
}
Como puedes ver, el nuestro Comparatores bastante sencillo. Sólo hay un método compare(): este es un método de interfaz Comparatorque debe implementarse. Toma dos objetos como entrada Cary compara su velocidad máxima de la forma habitual (por resta). Me gusta compareTo(), devuelve el número int, el principio de comparación es el mismo. ¿Cómo podemos usar esto? Muy simple:
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);
   }
}
Salida de consola:

[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}]
Simplemente creamos un objeto comparador y lo pasamos al método Collections.sort()junto con la lista a ordenar. Habiendo recibido un comparador como entrada, el método sort()no utilizará la clasificación natural definida en el método compareTo()de clase Car. En su lugar, aplicará el algoritmo de clasificación del comparador que se le haya pasado. ¿Qué ventajas nos aporta esto? En primer lugar, la compatibilidad con el código escrito. Creamos un método de clasificación nuevo y específico, manteniendo el actual, que se utilizará en la mayoría de los casos. No tocamos la clase en absoluto Car. Él Comparablepermaneció como estaba:
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()

}
En segundo lugar, la flexibilidad. Podemos agregar tantos tipos como queramos. Digamos, clasifique los autos por color, velocidad, peso o cuántas veces se usó el auto en las películas de Batman. Basta con crear uno adicional Comparator. ¡Eso es todo! Hoy aprendiste dos mecanismos muy importantes que utilizarás con frecuencia en proyectos reales en el trabajo. Pero, como sabes, la teoría sin práctica no es nada. ¡Por lo tanto, es hora de consolidar tus conocimientos y resolver varios problemas! :)
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION