¡Hola! Hoy hablaremos de un tema muy importante e interesante, a saber, comparar objetos entre sí iguales () en Java. Y de hecho, ¿en qué casos en Java el Objeto A será igual al Objeto B ? Intentemos escribir un ejemplo:
public class Car {
String model;
int maxSpeed;
public static void main(String[] args) {
Car car1 = new Car();
car1.model = "Ferrari";
car1.maxSpeed = 300;
Car car2 = new Car();
car2.model = "Ferrari";
car2.maxSpeed = 300;
System.out.println(car1 == car2);
}
}
Salida de consola:
false
Está bien, detente. ¿Por qué, de hecho, estos dos coches no son iguales? Les dimos las mismas propiedades, pero el resultado de la comparación es falso. La respuesta es simple. El operador ==
no compara las propiedades de los objetos, sino los enlaces. Incluso si dos objetos tienen 500 propiedades idénticas, el resultado de la comparación seguirá siendo falso. Después de todo, los enlaces car1
apuntan car2
a dos objetos diferentes , a dos direcciones diferentes. Imagine una situación en la que se comparan personas. Probablemente exista una persona en el mundo que tenga el mismo nombre, color de ojos, edad, altura, color de cabello, etc. que tú. Es decir, sois similares en muchos aspectos, pero aún así no sois gemelos y, sobre todo, no sois la misma persona. El operador aplica aproximadamente la misma lógica ==
cuando lo usamos para comparar dos objetos. Pero, ¿qué pasa si necesitas una lógica diferente en tu programa? Por ejemplo, si su programa simula un análisis de ADN. Debe comparar el código de ADN de dos personas y determinar que son gemelos.
public class Man {
int dnaCode;
public static void main(String[] args) {
Man man1 = new Man();
man1.dnaCode = 1111222233;
Man man2 = new Man();
man2.dnaCode = 1111222233;
System.out.println(man1 == man2);
}
}
Salida de consola:
false
Es lógico que el resultado fuera el mismo (al fin y al cabo, no cambiamos nada), ¡pero ahora no estamos contentos con eso! Efectivamente, en la vida real, el análisis de ADN es garantía cien por cien de que estamos ante gemelos. Pero nuestro programa y operador ==
nos dicen lo contrario. ¿Cómo podemos cambiar este comportamiento y asegurarnos de que si las pruebas de ADN coinciden, el programa producirá el resultado correcto? Para ello, se creó un método especial en Java: equals() .
Método igual() en Java
Al igual que el métodotoString()
que analizamos anteriormente, equals() pertenece a la clase, Object
la clase más importante en Java, de la que se derivan todas las demás clases. Sin embargo, el propio equals() no cambiará el comportamiento de nuestro programa de ninguna manera:
public class Man {
String dnaCode;
public static void main(String[] args) {
Man man1 = new Man();
man1.dnaCode = "111122223333";
Man man2 = new Man();
man2.dnaCode = "111122223333";
System.out.println(man1.equals(man2));
}
}
Salida de consola:
false
Exactamente el mismo resultado, entonces ¿por qué se necesita este método? :/ Es sencillo. El hecho es que ahora usamos este método tal como está implementado en la propia clase Object
. Y si entramos en el código de la clase Object
y miramos cómo se implementa este método y qué hace, veremos:
public boolean equals(Object obj) {
return (this == obj);
}
¡Esta es la razón por la cual el comportamiento de nuestro programa no ha cambiado! Dentro del método equals() de la clase Object
se encuentra la misma comparación de referencia, ==
. Pero el truco de este método es que podemos anularlo. ¡Anular significa escribir tu propio método equals() en nuestra clase Man
y hacer que se comporte como queremos! Ahora no estamos satisfechos de que el cheque man1.equals(man2)
haga esencialmente lo mismo que man1 == man2
. Esto es lo que haremos en esta situación:
public class Man {
int dnaCode;
public boolean equals(Man man) {
return this.dnaCode == man.dnaCode;
}
public static void main(String[] args) {
Man man1 = new Man();
man1.dnaCode = 1111222233;
Man man2 = new Man();
man2.dnaCode = 1111222233;
System.out.println(man1.equals(man2));
}
}
Salida de consola:
true
¡Un resultado completamente diferente! Al escribir nuestro propio método equals() en lugar del estándar, logramos el comportamiento correcto: ahora, si dos personas tienen el mismo código de ADN, el programa nos dice: “El análisis de ADN mostró que son gemelos” y devuelve verdadero. Al anular el método equals() en tus clases, puedes crear fácilmente la lógica de comparación de objetos necesaria. Hemos tocado la comparación de objetos sólo en términos generales. Todavía tendremos una gran conferencia separada sobre este tema por delante (puede leerla rápidamente ahora, si está interesado).
Comparación de cadenas en Java - Comparación de cadenas
¿Por qué tratamos las comparaciones de cadenas por separado de todo lo demás? Bueno, de hecho, las líneas en programación son una historia completamente diferente. En primer lugar, si tomamos todos los programas Java escritos por la humanidad, aproximadamente el 25% de los objetos que contienen están compuestos por ellos. Por tanto, este tema es muy importante. En segundo lugar, el proceso de comparar cadenas es realmente bastante diferente al de otros objetos. Veamos un ejemplo sencillo:
public class Main {
public static void main(String[] args) {
String s1 = "¡JavaRush es el mejor sitio para aprender Java!";
String s2 = new String("¡JavaRush es el mejor sitio para aprender Java!");
System.out.println(s1 == s2);
}
}
Salida de consola:
false
¿Pero por qué falso? Las líneas son exactamente iguales, palabra por palabra :/ Puedes suponer: ¡ esto se debe a que el operador ==
compara referencias! Después de todo, s1
tienen s2
diferentes direcciones en la memoria. Si se le ocurrió esta idea, rehagamos nuestro ejemplo:
public class Main {
public static void main(String[] args) {
String s1 = "¡JavaRush es el mejor sitio para aprender Java!";
String s2 = "¡JavaRush es el mejor sitio para aprender Java!";
System.out.println(s1 == s2);
}
}
Ahora también tenemos dos enlaces, pero el resultado ha cambiado a lo contrario: Salida de la consola:
true
¿Completamente confundido? :) Vamos a resolverlo. En realidad , el operador ==
compara direcciones en la memoria. Esta regla siempre funciona y no hay que dudar de ella. Esto significa que si s1 == s2
devuelve verdadero, estas dos cadenas tienen la misma dirección en la memoria. ¡Y efectivamente lo es! Es hora de familiarizarse con un área de memoria especial para almacenar cadenas: el grupo de cadenas ( String pool
). El grupo de cadenas es un área para almacenar todos los valores de cadena que crea en su programa. ¿Para qué fue creado? Como se mencionó anteriormente, las cuerdas ocupan una gran parte de todos los objetos. En cualquier programa grande, se crean muchas líneas. Para ahorrar memoria, esto es lo que se necesita String Pool
: se coloca allí una línea con el texto que necesita y, en el futuro, los enlaces recién creados se refieren a la misma área de memoria, no es necesario asignar memoria adicional cada vez. Cada vez que escribe String = “........”
, el programa comprueba si hay una línea con dicho texto en el grupo de cadenas. Si lo hay, no se creará uno nuevo. Y el nuevo enlace apuntará a la misma dirección en el grupo de cadenas donde se almacena esta cadena. Por lo tanto, cuando escribimos en el programa
String s1 = "¡JavaRush es el mejor sitio para aprender Java!";
String s2 = "¡JavaRush es el mejor sitio para aprender Java!";
el enlace s2
apunta exactamente al mismo lugar que s1
. El primer comando creó una nueva línea en el grupo de cadenas con el texto que necesitábamos, y cuando llegó el segundo, simplemente se refería a la misma área de memoria que s1
. Puedes hacer al menos 500 líneas más con el mismo texto, el resultado no cambiará. Detener. Pero, ¿por qué este ejemplo no nos funcionó antes?
public class Main {
public static void main(String[] args) {
String s1 = "¡JavaRush es el mejor sitio para aprender Java!";
String s2 = new String("¡JavaRush es el mejor sitio para aprender Java!");
System.out.println(s1 == s2);
}
}
Creo que, intuitivamente, ya adivinas cuál es el motivo :) Intenta adivinar antes de seguir leyendo. Puede ver que estas dos líneas se crearon de manera diferente. Uno es con la ayuda del operador new
y el segundo sin él. Ésta es precisamente la razón. El nuevo operador, al crear un objeto, le asigna por la fuerza una nueva área en la memoria . Y la línea creada con new
, no termina en String Pool
: se convierte en un objeto separado, incluso si su texto es exactamente el mismo que el de la misma línea de String Pool
'a. Es decir, si escribimos el siguiente código:
public class Main {
public static void main(String[] args) {
String s1 = "¡JavaRush es el mejor sitio para aprender Java!";
String s2 = "¡JavaRush es el mejor sitio para aprender Java!";
String s3 = new String("¡JavaRush es el mejor sitio para aprender Java!");
}
}
En la memoria se verá así: Y cada vez que se crea un nuevo objeto, new
se asignará una nueva área en la memoria, ¡incluso si el texto dentro de las nuevas líneas es el mismo! Parece que hemos solucionado el problema del operador ==
, pero ¿qué pasa con nuestro nuevo amigo, el método equals()?
public class Main {
public static void main(String[] args) {
String s1 = "¡JavaRush es el mejor sitio para aprender Java!";
String s2 = new String("¡JavaRush es el mejor sitio para aprender Java!");
System.out.println(s1.equals(s2));
}
}
Salida de consola:
true
Interesante. Sabemos exactamente qué s1
y s2
señalamos diferentes áreas de la memoria. Pero, sin embargo, el método equals() dice que son iguales. ¿Por qué? ¿Recuerda que anteriormente dijimos que el método equals() se puede anular en su clase para que compare objetos de la manera que necesita? Eso es lo que hicieron con la clase String
. Tiene un método igual a() anulado. Y no compara enlaces, sino la secuencia de caracteres en cadenas. Y si el texto en las cadenas es el mismo, no importa cómo se crearon y dónde se almacenan: en el grupo de cadenas o en un área de memoria separada. El resultado de la comparación será verdadero. Por cierto, Java le permite comparar cadenas correctamente sin distinguir entre mayúsculas y minúsculas. En una situación normal, si escribes una de las líneas, por ejemplo, en mayúsculas, el resultado de la comparación será falso:
public class Main {
public static void main(String[] args) {
String s1 = "¡JavaRush es el mejor sitio para aprender Java!";
String s2 = new String("JAVARUSH - ЛУЧШИЙ САЙТ ДЛЯ ИЗУЧЕНИЯ JAVA!");
System.out.println(s1.equals(s2));
}
}
Salida de consola:
false
Para este caso, la clase String
tiene un método equalsIgnoreCase()
. Si lo principal en tu comparación es la secuencia de caracteres específicos, y no su caso, puedes usarlo. Por ejemplo, esto será útil al comparar dos direcciones de correo electrónico:
public class Main {
public static void main(String[] args) {
String address1 = "Moscú, calle Académico Korolev, 12";
String address2 = new String("Г. МОСКВА, УЛ. АКАДЕМИКА КОРОЛЕВА, ДОМ 12");
System.out.println(address1.equalsIgnoreCase(address2));
}
}
En este caso, es obvio que estamos hablando de la misma dirección, por lo que utilizar el método equalsIgnoreCase()
será la decisión correcta.
Método String.intern()
La claseString
tiene otro método complicado: intern()
; El método intern()
funciona directamente con String Pool
'om. Si llama a un método intern()
en una cadena,:
- Mira para ver si hay una cadena con este texto en el grupo de cadenas.
- Si lo hay, devuelve un enlace al mismo en el grupo.
- De lo contrario, coloca una línea con este texto en el grupo de cadenas y le devuelve un enlace.
intern()
a la referencia de cadena que se creó mediante new, podemos compararla con la referencia de cadena de String Pool
'a mediante ==
.
public class Main {
public static void main(String[] args) {
String s1 = "¡JavaRush es el mejor sitio para aprender Java!";
String s2 = new String("¡JavaRush es el mejor sitio para aprender Java!");
System.out.println(s1 == s2.intern());
}
}
Salida de consola:
true
Anteriormente, cuando los comparamos sin intern()
, el resultado era falso. Ahora el método intern()
comprobó si había una línea con el texto "JavaRush - ¡el mejor sitio para aprender Java!" en el grupo de cuerdas. Por supuesto que está ahí: lo creamos cuando escribimos
String s1 = "¡JavaRush es el mejor sitio para aprender Java!";
Se comprobó que la referencia s1
y la referencia devuelta por el método s2.intern()
apuntan a la misma área en la memoria, y, por supuesto, lo hacen :) Para resumir, recuerde y use la regla principal: Para comparar cadenas, use SIEMPRE el método igual() ¡método! Al comparar cadenas, casi siempre te refieres a comparar su texto, no enlaces, áreas de memoria, etc. El método equals() hace exactamente lo que necesitas. Aquí te dejamos algunos enlaces para que estudies por tu cuenta:
GO TO FULL VERSION