lunes, 5 de septiembre de 2011

Java: Cadenas y otras cosas

¿Cuando son dos cadenas iguales? Suena fácil pero esto es más dificil de lo que se piensa.

Java es un lenguaje de programación que emplea un objeto para representar una cadena en vez de usar un arreglo de caracteres. Una cadena no es un tipo primitivo en Java.

Existen personas que comparan cadenas de caracteres del mismo modo que con los tipos primitivos (usando el operador ==).

Pero ¿Es eso correcto?

Todo programador Java sabe como comparar tipos primitivos (int, char, boolean, etc.) con el operador ==.


public class Igualdad {
 public static void main(String[] args) {
  int a = 10, b = 10;
  if(a == b) {
   System.out.println("Igual");}
  }
 }

Pero ¿Qué pasa con los objetos de cadena (String)?

El siguiente código muestra que podemos comparar objetos String del mismo modo que con los tipos primitivos.


public class IgualdadCadenas {
 public static void main(String[] args) {
  String cadena = "abc", cadena2 = "abc";
  if(cadena == cadena2) {
   System.out.println("Igual");
  }
 }
}

Y funciona. Este tipo de experiencia inicial con la comparacion de cadenas puede llevar por mal camino a los principiantes.

Así que ¿Es verdad que siempre puedes comparar objetos String con el operador relacional de igualdad?

Si usas el operador = = para comparar objetos String, es cuestión de tiempo para que tengas problemas con ello. Por ejemplo, intenta lo siguiente:


public class IgualdadCadena {
 public static void main(String[] args) {
  String cadena1 = "abc", cadena2 = "def";
  if(cadena1 + cadena2 == "abcdef") {
   System.out.println("Igual");
  }
 }
}

En este caso la comparación de Strings no funciona.

La comparación siguiente:

cadena1 + cadena2 == "abcdef"
 
Pareciera ser falsa, cuando por toda lógica razonable debe ser verdad.

Aquí otro ejemplo de que no funciona:


public class IgualdadCadena {
 public static void main(String[] args) {
  String cadena1 = "abc";
  String cadena2 = new String("abc");
  if(cadena1 == cadena2) {
   System.out.println("Equal");
  }
 }
}

De nuevo el resultado es falso pero se espera que sea verdadero dado que los Strings son los mismos.

¿Por qué estas comparaciones son falsas cuando se espera que sean verdaderas?

Muchos programadores Java emplean el operador = = para comparar Strings, sin embargo, si deseas comparar el valor de dos objetos Strings es necesario emplear un método.

El método equals()  compara el texto de dos objetos String. Si el texto es el mismo, regresará un valor True, de otro modo, regresará False. Así el siguiente código funciona como se espera:


public class IgualdadCadena {
 public static void main(String[] args) {
  String cadena1 = "abc", cadena2 = "def";
  if((cadena1 + cadena2).equals("abcdef")) {
   System.out.println("Igual");
  }
 }

Pero ¿Por qué el operador = = a veces funciona y otras no?

Esto es porque los objetos String en Java son inmutables y el operador = = está comparando referencias de objeto, no el texto de los Strings. Estos dos hechos trabajan juntos ya a veces hace que el operador = = parezca que realiza la comparación correcta. Por lo tanto si creas dos cadenas usando la sintaxis siguiente:


String cadena1 = "abc";
String cadena2 = "abc";
 
Java no creará dos objetos String con el mismo valor sino que creará solo uno conteniendo el valor "abc". Así cadena1 y cadena2 estarán referenciando al mismo objeto en memoria. Esta es la razón por la que:

cadena1 = cadena2

Es verdad. Esto es porque el operador = = revisa si ambos objetos referencian al mismo objeto en memoria y esto también quiere decir que ambos objetos contienen el  mismo valor.

Sin embargo si se usa un constructor de String entonces se crea un nuevo espacio de memoria para el nuevo objeto. Como lo siguiente:

String string1 = "abc";
String string2 = new String("abc");

Tenemos dos objetos String que contienen el mismo texto pero ahora:


string1 == string2

Es falso porque las dos variables referencían a disntintos objetos. Lo mismo sucede si se compara el resultado sobre la marcha o comparar una cadena con un literal. Por ejemplo:


string1 + string2 == "abcdef"

Es falso por dos razones. Primero, string1 y string2 son concatenados en un nuevo objeto y "abcdef" es aún un objeto String y por lo tanto no pueden ser igual las referencias a objeto.

Siempre se debe emplear el método equals() si se desea comparar el texto de dos objetos String. Quizá se creerá que es una desventaja el no poder usar el operador = = para compara objetos String. No hay que olvidar que los objetos String no son tipos primitivos y por lo tanto emplea referencia semántica no valores semánticos.

Así se emplea el operador = = para saber si dos objetos String son el mismo objeto en memoria además esto asegura de que ambos tienen el mismo texto.

No hay comentarios:

Publicar un comentario