CodeGym/Blog Java/Random-ES/Interfaz del comparador de Java
Autor
Artem Divertitto
Senior Android Developer at United Tech

Interfaz del comparador de Java

Publicado en el grupo Random-ES
Los perezosos no son los únicos que escriben sobre comparadores y comparaciones en Java. No soy perezoso, así que por favor ama y quéjate de otra explicación más. Espero que no sea superfluo. Y sí, este artículo es la respuesta a la pregunta: "¿ Puedes escribir un comparador de memoria? " Espero que todos puedan escribir un comparador de memoria después de leer este artículo. Interfaz del comparador Javas - 1

Introducción

Como sabes, Java es un lenguaje orientado a objetos. Como resultado, es costumbre manipular objetos en Java. Pero tarde o temprano, te enfrentas a la tarea de comparar objetos en función de alguna característica. Por ejemplo : supongamos que tenemos algún mensaje descrito por la Messageclase:
public static class Message {
    private String message;
    private int id;

    public Message(String message) {
        this.message = message;
        this.id = new Random().nextInt(1000);
    }
    public String getMessage() {
        return message;
    }
    public Integer getId() {
        return id;
    }
    public String toString() {
        return "[" + id + "] " + message;
    }
}
Coloque esta clase en el compilador de Java de Tutorialspoint . No olvide agregar las declaraciones de importación también:
import java.util.Random;
import java.util.ArrayList;
import java.util.List;
En el mainmétodo, crea varios mensajes:
public static void main(String[] args){
    List<Message> messages = new ArrayList();
    messages.add(new Message("Hello, World!"));
    messages.add(new Message("Hello, Sun!"));
    System.out.println(messages);
}
Pensemos en qué haríamos si quisiéramos compararlos. Por ejemplo, queremos ordenar por id. Y para crear un orden, necesitamos comparar de alguna manera los objetos para entender qué objeto debe ir primero (es decir, el más pequeño) y cuál debe seguir (es decir, el más grande). Comencemos con una clase como java.lang.Object . Sabemos que todas las clases heredan implícitamente la Objectclase. Y esto tiene sentido porque refleja el concepto de que "todo es un objeto" y proporciona un comportamiento común para todas las clases. Esta clase dicta que cada clase tiene dos métodos: → hashCode El hashCodemétodo devuelve algo numérico (int) representación del objeto. ¿Qué significa eso? Significa que si crea dos instancias diferentes de una clase, entonces deberían tener hashCodes diferentes. La descripción del método dice tanto: "En la medida en que sea razonablemente práctico, el método hashCode definido por la clase Object devuelve enteros distintos para objetos distintos". En otras palabras, para dos instances diferentes, debe haber hashCodes diferentes. Es decir, este método no es adecuado para nuestra comparación. → equals. El equalsmétodo responde a la pregunta "¿son estos objetos iguales?" y devuelve un boolean." Por defecto, este método tiene el siguiente código:
public boolean equals(Object obj) {
    return (this == obj);
}
Es decir, si este método no se anula, esencialmente dice si las referencias de objetos coinciden o no. Esto no es lo que queremos para nuestros mensajes, porque estamos interesados ​​en los identificadores de mensajes, no en las referencias de objetos. E incluso si anulamos el equalsmétodo, lo más que podemos esperar es saber si son iguales. Y esto no es suficiente para que determinemos el orden. Entonces, ¿qué necesitamos entonces? Necesitamos algo que se compare. El que compara es un Comparator. Abra la API de Java y busque Comparator . De hecho, hay una java.util.Comparatorinterfaz java.util.Comparator and java.util.Comparable Como puede ver, tal interfaz existe. Una clase que lo implementa dice: "Implemento un método que compara objetos". Lo único que realmente necesita recordar es el contrato de comparación, que se expresa de la siguiente manera:
Comparator returns an int according to the following rules:

It returns a negative int if the first object is smaller
It returns a positive int if the first object is larger
It returns zero if the objects are equal
Ahora vamos a escribir un comparador. Tendremos que importar java.util.Comparator. Después de la declaración de importación, agregue lo siguiente al mainmétodo: Comparator<Message> comparator = new Comparator<Message>(); Por supuesto, esto no funcionará, porque Comparatores una interfaz. Entonces agregamos llaves {}después de los paréntesis. Escribe el siguiente método dentro de las llaves:
public int compare(Message o1, Message o2) {
    return o1.getId().compareTo(o2.getId());
}
Ni siquiera necesitas recordar la ortografía. Un comparador es aquel que realiza una comparación, es decir, compara. Para indicar el orden relativo de los objetos, devolvemos un int. Eso es básicamente todo. Bonito y fácil. Como puede ver en el ejemplo, además de Comparator, hay otra interfaz, java.lang.Comparableque requiere que implementemos el compareTométodo. Esta interfaz dice: "una clase que me implementa hace posible comparar instancias de la clase". Por ejemplo, Integerla implementación de compareTo es la siguiente:
(x < y) ? -1 : ((x == y) ? 0 : 1)
Java 8 introdujo algunos cambios agradables. Si echa un vistazo más de cerca a la Comparatorinterfaz, verá la @FunctionalInterfaceanotación encima de ella. Esta anotación es para fines informativos y nos dice que esta interfaz es funcional. Esto significa que esta interfaz tiene solo 1 método abstracto, que es un método sin implementación. ¿Qué nos da esto? Ahora podemos escribir el código del comparador así:
Comparator<Message> comparator = (o1, o2) -> o1.getId().compareTo(o2.getId());
Nombramos las variables entre paréntesis. Java verá que debido a que solo hay un método, entonces el número requerido y los tipos de parámetros de entrada son claros. Luego usamos el operador de flecha para pasarlos a esta parte del código. Además, gracias a Java 8, ahora tenemos métodos predeterminados en las interfaces. Estos métodos aparecen por defecto cuando implementamos una interfaz. La Comparatorinterfaz tiene varios. Por ejemplo:
Comparator moreImportant = Comparator.reverseOrder();
Comparator lessImportant = Comparator.naturalOrder();
Hay otro método que hará que su código sea más limpio. Eche un vistazo al ejemplo anterior, donde definimos nuestro comparador. ¿Qué hace? Es bastante primitivo. Simplemente toma un objeto y extrae algún valor que sea "comparable". Por ejemplo, Integerimplements comparable, por lo que podemos realizar una operación compareTo en los valores de los campos de identificación del mensaje. Esta función de comparación simple se puede escribir así:
Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
En otras palabras, tenemos un Comparatorque compara así: toma objetos, usa el getId()método para obtener un Comparablede ellos y luego los usa compareTopara comparar. Y no hay construcciones más horribles. Y finalmente, quiero señalar una característica más. Los comparadores se pueden encadenar. Por ejemplo:
Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
comparator = comparator.thenComparing(obj -> obj.getMessage().length());

Solicitud

Declarar un comparador resulta bastante lógico, ¿no crees? Ahora tenemos que ver cómo y dónde usarlo. → Collections.sort(java.util.Collections) Podemos, por supuesto, ordenar las colecciones de esta manera. Pero no todas las colecciones, solo listas. No hay nada inusual aquí, porque las listas son el tipo de colecciones en las que accedes a los elementos por su índice. Esto permite que el segundo elemento se intercambie con el tercer elemento. Es por eso que el siguiente método de clasificación es solo para listas:
Comparator<Message> comparator = Comparator.comparing(obj -> obj.getId());
Collections.sort(messages, comparator);
Arrays.sort(java.util.Arrays) Las matrices también son fáciles de ordenar. Nuevamente, por la misma razón: se accede a sus elementos mediante index. → Descendants of java.util.SortedSet and java.util.SortedMap Lo recordará Sety Mapno garantiza el orden en que se almacenan los elementos. PERO, tenemos implementaciones especiales que garantizan el orden. Y si los elementos de una colección no se implementan java.util.Comparable, podemos pasar Comparatora su constructor:
Set<Message> msgSet = new TreeSet(comparator);
Stream API En Stream API, que apareció en Java 8, los comparadores le permiten simplificar el trabajo con elementos de flujo. Por ejemplo, supongamos que necesitamos una secuencia de números aleatorios del 0 al 999, ambos inclusive:
Supplier<Integer> randomizer = () -> new Random().nextInt(1000);
Stream.generate(randomizer)
    .limit(10)
    .sorted(Comparator.naturalOrder())
    .forEach(e -> System.out.println(e));
Podríamos detenernos aquí, pero hay problemas aún más interesantes. Por ejemplo, suponga que necesita preparar un correo electrónico Map, donde la clave es una identificación de mensaje. Además, queremos ordenar estas claves, por lo que comenzaremos con el siguiente código:
Map<Integer, Message> collected = Arrays.stream(messages)
                .sorted(Comparator.comparing(msg -> msg.getId()))
                .collect(Collectors.toMap(msg -> msg.getId(), msg -> msg));
De hecho, tenemos un HashMapaquí. Y como sabemos, no garantiza ningún pedido. Como resultado, nuestros elementos, que fueron ordenados por id, simplemente pierden su orden. No es bueno. Tendremos que cambiar un poco nuestro colector:
Map<Integer, Message> collected = Arrays.stream(messages)
                .sorted(Comparator.comparing(msg -> msg.getId()))
                .collect(Collectors.toMap(msg -> msg.getId(), msg -> msg, (oldValue, newValue) -> oldValue, TreeMap::new));
El código ha comenzado a parecer un poco más aterrador, pero ahora el problema se resuelve correctamente. Lea más sobre las diversas agrupaciones aquí: Puedes crear tu propio colector. Lea más aquí: "Creación de un recopilador personalizado en Java 8" . Y se beneficiará de leer la discusión aquí: "Lista de Java 8 para mapear con flujo" .

Trampa de caída

Comparatory Comparableson buenos Pero hay un matiz que debes recordar. Cuando una clase realiza la clasificación, espera que su clase se pueda convertir a un archivo Comparable. Si este no es el caso, recibirá un error en tiempo de ejecución. Veamos un ejemplo:
SortedSet<Message> msg = new TreeSet<>();
msg.add(new Message(2, "Developer".getBytes()));
Parece que nada está mal aquí. Pero, de hecho, en nuestro ejemplo, fallará con un error: java.lang.ClassCastException: Message cannot be cast to java.lang.Comparable Y todo porque intentó ordenar los elementos ( SortedSetdespués de todo, es un )... pero no pudo. No olvide esto cuando trabaje con SortedMapy SortedSet.
Comentarios
  • Populares
  • Nuevas
  • Antiguas
Debes iniciar sesión para dejar un comentario
Esta página aún no tiene comentarios