1. Comparando objetos en Java
En Java, los objetos se pueden comparar tanto por referencia como por valor.
Comparando referencias
Si dos variables apuntan al mismo objeto en la memoria, entonces las referencias almacenadas en estas variables son iguales. Si compara estas variables usando el operador de igualdad ( ==
), obtiene verdadero y ese resultado tiene sentido. Todo es simple aquí.
Código | Salida de consola |
---|---|
|
|
Comparando por valor
Pero a menudo puede encontrar situaciones en las que dos variables se refieren a dos objetos distintos que son idénticos. Por ejemplo, dos objetos de cadenas diferentes que contienen el mismo texto.
Para determinar si diferentes objetos son idénticos, utilice el equals()
método. Por ejemplo:
Código | Salida de consola |
---|---|
|
|
El equals
método no se limita a la String
clase. Cada clase lo tiene.
Incluso las clases que escribes por tu cuenta, y he aquí por qué.
2. Object
clase
Todas las clases en Java heredan la Object
clase. A los creadores de Java se les ocurrió este enfoque.
Y si una clase hereda la Object
clase, entonces gana todos los métodos de la Object
clase. Y esta es una de las principales consecuencias de la herencia.
En otras palabras, cada clase tiene los métodos de la Object
clase, incluso si su código no los menciona.
Estos métodos heredados incluyen métodos relacionados con la comparación de objetos. Estos son los métodos equals()
y hashCode()
.
Código | En realidad, esto es lo que tendremos: |
---|---|
|
|
En el ejemplo anterior, creamos una Person
clase simple con parámetros de nombre y edad, pero no un único método. Pero debido a que todas las clases heredan la Object
clase, la Person
clase automáticamente tiene dos métodos:
Método | Descripción |
---|---|
|
Compara el objeto actual y el objeto pasado |
|
Devuelve el código hash del objeto actual |
Resulta que absolutamente todos los objetos tienen el equals
método, y los objetos de diferentes tipos se pueden comparar entre sí. Dicho código se compilará y funcionará perfectamente.
Código | Salida de consola |
---|---|
|
|
|
|
3. equals()
método
El equals()
método, heredado de la Object
clase, implementa el algoritmo más simple para comparar el objeto actual con los objetos pasados: solo compara las referencias a los objetos.
Obtiene el mismo resultado si solo compara Person
variables en lugar de llamar al equals()
método. Ejemplo:
Código | Salida de consola |
---|---|
|
|
Cuando equals
se llama al método a
, simplemente compara la referencia almacenada en la a
variable con la referencia almacenada en la b
variable.
Sin embargo, la comparación funciona de manera diferente para la String
clase. ¿Por qué?
Porque la gente que creó la String
clase escribió su propia implementación del equals()
método.
Implementación del equals()
método
Ahora escribamos nuestra propia implementación del método equals en la Person
clase. Consideraremos 4 casos principales.
equals
método, siempre toma un Object
objeto como argumento
Escenario 1 : el mismo objeto en el que equals
se llama al método también se pasa al equals
método. Si las referencias del objeto actual y el objeto pasado son iguales, el método debe devolver true
. Un objeto es igual a sí mismo.
En código se verá así:
Código | Descripción |
---|---|
|
Compara referencias |
Escenario 2 : null
se pasa al equals
método: no tenemos nada con lo que comparar. El objeto en el que equals
se llama al método definitivamente no es nulo, por lo que debemos regresar false
en este caso.
En código se verá así:
Código | Descripción |
---|---|
|
Comparar referencias ¿ Es el objeto pasado null ? |
Escenario 3 : Person
se pasa al equals
método una referencia a un objeto que no es a. ¿ Es el Person
objeto igual al no- Person
objeto? Esa es una pregunta para que el desarrollador de la Person
clase decida como él o ella quiera.
Pero, por lo general, los objetos deben ser de la misma clase para ser considerados iguales. Por lo tanto, si se pasa algo que no sea un objeto de la Person
clase a nuestro método equals, siempre devolveremos false
. ¿Cómo se puede comprobar el tipo de un objeto? Así es, usando el instanceof
operador.
Así es como se ve nuestro nuevo código:
Código | Descripción |
---|---|
|
Comparar referencias ¿ Es el objeto pasado null ? Si el objeto pasado no es un Person |
4. Comparar dos Person
objetos
¿Con qué terminamos? Si hemos llegado al final del método, entonces tenemos una Person
referencia de objeto que no es null
. Entonces lo convertimos a Person
y comparamos los datos internos relevantes de ambos objetos. Y ese es nuestro cuarto escenario .
Código | Descripción |
---|---|
|
Comparar referencias ¿ Es el objeto pasado null ? Si el objeto pasado no es un Person Typecasting |
¿Y cómo comparas dos Person
objetos? Son iguales si tienen el mismo nombre ( name
) y edad ( age
). El código final se verá así:
Código | Descripción |
---|---|
|
Comparar referencias ¿ Es el objeto pasado null ? Si el objeto pasado no es un Person Typecasting |
Pero eso no es todo.
Primero, el campo de nombre es un String
, por lo que debe comparar el campo de nombre llamando al equals
método.
this.name.equals(person.name)
En segundo lugar, el name
campo puede ser null
: en ese caso, no puede llamarlo equals
. Necesita un cheque adicional para null
:
this.name != null && this.name.equals(person.name)
Dicho esto, si el campo de nombre está null
en ambos Person
objetos, los nombres siguen siendo iguales.
El código para el cuarto escenario podría verse así:
|
Si las edades no son iguales, inmediatamente return false Si this.name es igual a null , no tiene sentido comparar usando el equals método. Aquí el segundo name campo es igual a null , o no lo es. Compare los dos campos de nombre usando el equals método. |
5. hashCode()
método
Además del equals
método, que pretende realizar una comparación detallada de todos los campos de ambos objetos, existe otro método que puede utilizarse para una comparación imprecisa pero muy rápida: hashCode()
.
Imagina que estás ordenando alfabéticamente una lista de miles de palabras y necesitas comparar repetidamente pares de palabras. Y las palabras son largas, compuestas de muchas letras. En términos generales, tal comparación llevaría mucho tiempo.
Pero se puede acelerar. Supongamos que tenemos palabras que comienzan con letras diferentes: inmediatamente queda claro que son diferentes. Pero si comienzan con las mismas letras, todavía no podemos decir cuál será el resultado: las palabras pueden resultar iguales o diferentes.
El hashCode()
método funciona utilizando un principio similar. Si lo llama en un objeto, devuelve algún número, análogo a la primera letra de una palabra. Este número tiene las siguientes propiedades:
- Los objetos idénticos siempre tienen el mismo código hash
- Diferentes objetos pueden tener el mismo código hash, o sus códigos hash pueden ser diferentes
- Si los objetos tienen códigos hash diferentes, entonces los objetos son definitivamente diferentes
Para hacer esto aún más claro, reformulemos estas propiedades en términos de palabras:
- Las palabras idénticas siempre tienen las mismas primeras letras.
- Diferentes palabras pueden tener las mismas primeras letras, o sus primeras letras pueden ser diferentes
- Si las palabras tienen primeras letras diferentes, entonces las palabras son definitivamente diferentes
La última propiedad se utiliza para acelerar la comparación de objetos:
Primero, se calculan los códigos hash de los dos objetos. Si estos códigos hash son diferentes, entonces los objetos son definitivamente diferentes y no hay necesidad de compararlos más.
Pero si los códigos hash son los mismos, todavía tenemos que comparar los objetos usando el método de igualdad.
6. Contratos en clave
El comportamiento descrito anteriormente debe ser implementado por todas las clases en Java. Durante la compilación, no hay forma de comprobar si los objetos se comparan correctamente.
Los programadores de Java tienen un acuerdo universal de que si escriben su propia implementación del método equals() y, por lo tanto, anulan la implementación estándar (en la Object
clase), también deben escribir su propia implementación del hashCode()
método de tal manera que se cumplan las reglas antes mencionadas. satisfecho.
Este arreglo se llama contrato .
Si implementa solo el equals()
o solo el hashCode()
método en su clase, entonces está violando gravemente el contrato (ha roto el acuerdo). No hagas esto.
Si otros programadores usan su código, es posible que no funcione correctamente. Además, utilizará un código que se basa en el cumplimiento de los contratos anteriores.
Al buscar un elemento, todas las colecciones de Java primero comparan los códigos hash de los objetos y solo luego realizan una comparación utilizando el equals
método.
Eso significa que si le da a su propia clase un equals
método pero no escribe su propio hashCode()
método o lo implementa incorrectamente, es posible que las colecciones no funcionen correctamente con sus objetos.
Por ejemplo, puede agregar un objeto a una lista y luego buscarlo usando el contains()
método, pero es posible que la colección no encuentre su objeto.