Comparando tipos en Java: ==, .equals y hashcode

Una de las preguntas más comunes en las entrevistas de trabajo donde nos postulamos como programador Java consiste en responder la diferencia entre comprar con == o equals en Java. Es que efectivamente es diferente en dependencia del tipo de datos que esta sujeto a la comparación. En esta entrada vamos a ver como funciona cada forma de comparar.

Tipos en Java

Los tipos de datos son el recurso de la programación que le dice a la variable o identificador lo que ella puede almacenar, contener. Existen dos categorías de tipos de datos principales: los primitivos y las clases, en alguna que otra bibliografía también incluyen los wrappers o contenedores, pero en esencia son clases de igual manera. Veamos cada uno:

Tipos primitivos: Son los tipos más básicos, se clasifican en númericos (enteros [byte, short, int, long] y de punto flotante [float, double]), de carácter [char] y lógico o booleano [boolean].

Tipos de datos estructurados, clases o contenedores: Son tipos que están compuestos por datos primitivos o por una combinación de ellos mismos propiamente, cuando se crea una instancia de este tipo de datos se conoce como objetos (en el paradigma orientado a objetos).

La principal diferencia desde el punto de vista del manejo interno de la JVM para los datos primitivos y clases, es la forma en como estos se almacenan en la memoria en tiempo de ejecución.

Los tipos primitivos persisten en un dirección de memoria, mientras las clases en lugar de estar en una dirección de memoria, puesto que son compuestos lo que tienen es en la dirección de memoria una referencia al objeto en sí.

Entonces mientras el dato primitivo esta directo en una dirección de memoria cada vez que lo cambiamos estamos accediendo directamente a él, mientras que el objeto lo hacemos a través de una referencia.

Los dos últimos párrafos son fundamentales para entender porque no todos los tipos pueden ser comparados de la misma forma.

Conceptos claves ==, .equals y hashcode

Operador ==: El operador de igualdad (==) en Java es un recurso que provee el lenguaje para comparar tipos; su funcionamiento es limitado a comparar valores dado sus identificadores, si los identificadores en la dirección de memoria tienen el mismo valor, será verdadero, en caso contrario false. Como los datos primitivos se almacenan en la dirección de memoria si ambos son el mismo valor entonces pueden compararse con este operador directamente.

Función .equals (Object o) : Todos los tipos de datos que creamos en Java, de forma intrínseca, aunque no lo pongamos heredan de la clase Object. Esta clase posee entre sus métodos equals(Object o), que posibilita hacer override para comparar el Object o pasado con el actual (this), la forma en que implementemos esta función determinará como se comparar los objetos.

Función .hashCode(): Es parte igual de Object, sirve para comparar objetos de una forma más rápida en estructuras Hash ya que únicamente nos devuelve un número entero. Cuando Java compara dos objetos en estructuras de tipo hash (HashMap etc) primero invoca al método hashcode y luego el equals. Si los métodos hashcode de cada objeto devuelven diferente hash no seguirá comparando y considerará a los objetos distintos. En el caso en el que ambos objetos compartan el mismo hashcode Java invocará al método equals() y revisará a detalle si se cumple la igualdad.

Los métodos .equals y .hashCode siempre deben implementarse de conjunto.

Ejemplo de .equals(Object o) y .hashCode()

El siguiente ejemplo muestra un ejemplo de implementación de los métodos para comparar dos objetos de la clase en cuestión:


public class IngredientInt {
    private MessagesKey messagesKey;
    private String name;
    public IngredientInt() {
    }
    public IngredientInt(MessagesKey messagesKey, String name) {
        this.messagesKey = messagesKey;
        this.name = name;
    }
    public MessagesKey getMessagesKey() {
        return messagesKey;
    }
    public void setMessagesKey(MessagesKey messagesKey) {
        this.messagesKey = messagesKey;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        IngredientInt that = (IngredientInt) o;
        return messagesKey == that.messagesKey &&
            name.equals(that.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(messagesKey, name);
    }
}

Comparación sobre conjuntos (Set) en Java

Los Java existe el tipo Set que permite la representación de conjuntos, que no permiten tipos duplicados, resulta interesante saber la relación entre los tipos Set y el método .equals(Object o).

Cuando creamos un objeto de tipo Set<T> y comenzamos a añadir elementos, el valor del tipo T indicará la forma en que se compararan los elementos sobre el tipo Set para saber si son iguales y se deben añadir o sino se añaden por ser duplicados.

Cuando en Set<T>, el tipo T es primitivo, internamente se comparará con el operador ==, en cambio cuando el tipo T es una clase o compuesto, entonces se invocará a equals(Object o), esto es muy importante tenerlo en cuenta.

Siempre que vamos a crear un conjunto de un tipo de clase, tenemos que implementar .equals(Object o) para esperar resultados correctos y evitar duplicados.

Espero te haya sido útil este artículo, si fue así puedes invitarme un café.

Leave a Reply

Your email address will not be published. Required fields are marked *