[Las cosas que no interesan]
lunes oct 31, 2005
[Apuntes]: String y StringBuffer
Una de las clases más importantes de Java es la clase String. La usamos todo el tiempo, todos los objetos tienen un método .toString() que indica su nombre completamente cualificado y su código hash, los datos de entrada se toman en forma de String la mayoría de las veces y así mismo representamos los resultados, etc; esto hace que sea de suma importancia en el aprendizaje del lenguaje y como no en los exámenes de certificación. StringBuffer se puede decir que tiene el mismo objetivo de String a la hora de representar los datos, pero está diseñado para contribuir con la optimización en el uso de memoria.
He estado estudíandolas y pongo por acá algunos apuntes; esperemos que algún día haya material suficiente para hacer un artículo completo.
java.lang.String
La clase String implementa el patrón de diseño inmutable y se puede decir que esta fortaleza igualmente se convierte en el quebradero que cabeza o en una de las fuentes más comunes de errores, pues en tiempo de ejecución es un objeto que no tiene el comportamiento habitual.
El patrón inmutable indica un objeto que no puede ser modificado y eso
es precisamente lo que sucede con la clase String, cuando creamos un objeto
este no puede cambiar más en el transcurso del programa y cada instrucción
que parezca lo debe modificar, en realidad está creando un nuevo objeto,
así que siempre debemos estar atentos con la respectiva referencia; igualmente
cuando hacemos comparaciones hay que tener mucho cuidado al emplear el operador
== o el método equals(), pues una cosa es comparar
las referencias y otra los valores.
Veamos la siguiente porción de código:
String s1 = "pisale ";
s1.concat("pepe");
System.out.println(s1);
Dará como salida "pisale". Aunque es cierto que
el método concat concatena el valor pasado como parámetro
a la cadena de caracteres, el objeto no puede cambiar y por tanto un nuevo objeto
de tipo String es creado con el valor "pisale pepe" y
como no es referenciado, simplemente pasa al pool donde están las cadenas
de caracteres que se han ido creando durante la ejecución del programa.
Obtenemos un resultado diferente si de nuevo asignamos la cadena generada a la variable s1:
String s1 = "pisale ";
s1 = s1.concat("pepe");
System.out.println(s1);
Aqí la salida generada si será "pisale pepe"
Este mismo comportamiente obtendremos con todos los métodos que modifiquen
una cadena, tales como toUpperCase(), toLowerCase(),
replace(char c1, char c2), etc.
Ahora, si queremos comparar dos String hay que saber que == compara
referencias a objetos, no valores; esto quiere decir que aunque dos cadenas
de caracteres tengan como valor "pisale", puede que al
compararlas la máquina virtual nos diga que estas no son iguales, ya
que no es suficiente que tengan el mismo valor sino que deben estar apuntando
hacia el mismo objeto almacenado en memoria, por ejemplo:
String s1 = "pepe";
String s2 = "pepe";
System.out.println("s1 == s2");
if (s1 == s2){
System.out.println("Somos igualitos!!");
}else{
System.out.println("Somos diferentes");
}
Dará como salida "somos igualitos!!" aún al modificarlo así:
String s1 = "pepe";
String s2 = "pe"+"pe";
System.out.println("s1 == s2");
if (s1 == s2){
System.out.println("Somos igualitos!!");
}else{
System.out.println("Somos diferentes");
}
El compilador automáticamente concatena las cadenas "pe" y
"pe". Esto forma una cadena "pepe" igual a la
que está apuntando s1 y como las toma del pool de String, ambas variables quedarán
con la misma referencia "pepe", por tanto la salida de este código
es "somos igualitos".
Otra cosa sucede cuando ejecutamos lo siguiente:
String s1 = "pepe";
String s2 = "pe";
String s3 = "pe";
String s4 = s2 + s3;
System.out.println("s1 == s2 --> "+s1 + " == "+s4);
if (s1 == s4){
System.out.println("Somos igualitos!!");
}else{
System.out.println("Somos diferentes");
}
Aquí el resultado de la comparación es "somos diferentes".¿Por
qué? la situación acá es que el objeto s4 se está
creando a partir de otros dos objetos para lo cual la máquina virtual
necesita crear un tercer objeto que finalmente tendrá el valor "pepe",
ojo!, sólamente el valor, no la misma referencia. Igual sucede con objetos
pasados por la línea de parámetros (cuando trabajamos por consola),
estos no se deben comparar con el operador == pues al parecer no se almacenan en
el pool de String.
¿Y qué hay con el método equals?
Bien, este método si compara los valores que contienen las cadenas, la referencia no importa. El siguiente código:
String s1 = "pepe";
String s2 = "pe";
String s3 = "pe";
String s4 = s2 + s3;
if (s1.equals(s4)){
System.out.println("iguales con equals");
}else{
System.out.println("diferentes???");
}
Tiene la salida: "iguales con equals" y lo mismo sucedería
con todas las comparaciones que hemos hecho anteriormente usando el operador
==.
Como última observación sobre String hay que anotar que aún si el mismo objeto se crea de forma diferente, la comparación de referencias no funcionará; por ejemplo:
String s1 = "pisale pepe";
String s2 = new String(s1);
if (s1 == s2){
System.out.println("Iguales con ==");
}else{
System.out.println("diferentes con ==");
}
if (s1.equals(s2)){
System.out.println("iguales con equals");
}else{
System.out.println("diferentes con equals");
}
Aunque usamos el objeto s1 para crear s2, la salida de este programa será:
diferentes con == iguales con equals
StringBuffer
La clase StringBuffer está diseñada para manejar cadenas de caracteres que deban cambiar varias veces durante la ejecucción de un programa. Veamos esta línea de código con:
String st = "pisale ";
st = st.concat("pepe");
Aquí se ha creado una cadena de caracteres "pisale pepe" a la que está apuntando la variable st; lo malo es que el valor "pisale " creado inicialmente se va para el pool de String y sigue consumiendo memoria. Si pensamos en una aplicación grande o mediana y el montón de instrucciones similares que tenemos, nos damos cuenta de una de las desventajas del patrón inmutable y es su "excesivo" consumo de memoria, creando objetos por doquier.
La clase StringBuffer se pensó para solucionar esto y su principal característica es que representa una cadena de caracteres que puede cambiar, aquí no hay referencias perdidas y todas las modificaciones quedarán en el mismo objeto que estamos trabajando. Por ejemplo:
StringBuffer sb = new StringBuffer("pisale");
sb.append(" pepe");
Aquí en realidad la variable sb al final tendrá el valor "pisale
pepe" y tan solo se habrá creado un objeto que se luego se
modifica. Comparando ambas clases:
StringBuffer sb = new StringBuffer("pisale");
sb.append(" pepe");
sb.append(" con fuerza");
System.out.println("StringBuffer: "+sb);
String st = "pisale";
st.concat(" pepe");
st.concat(" con fuerza");
System.out.println("String: "+st);
Obtenemos:
StringBuffer: pisale pepe con fuerza String: pisale
Y mientras usando String hemos creado tres objetos("pisale", " pepe", " con fuerza"), con StringBuffer solo hemos creado uno y lo hemos modificado hasta obtener una sola cadena "pisale pepe con fuerza".
StringBuffer también tiene otros métodos bien "simpáticos"
y seguramente útiles en muchos casos como reverse que conviernte
"arroz" en "zorro" o insert
que permite insertar una cadena entre las posiciones indicadas, pero creo que
ya sería demasiado tratarlas en esta publicación, mejor las dejamos para
otro día.
Esto es todo por ahora, si hay algún error o sugerencia para que esto sea un artículo mejor redactado y estructurado lo tendré en cuenta,. por favor decirlo en los comentarios; si no, pues dejamos así y en la próxima hablo un poco sobre la clase Math y sus métodos.
Posted at 09:26PM oct 31, 2005 by Carlos Alexander Zuluaga in Java | Comentarios[4]


Bueno, el comentario es más bien es un truquito que quizás no viene al caso, pero en todo caso lo suelto.
En la mayoría de los ejemplos (o por la naturaleza humana en si misma) se tiene a usar:
variable.concat("constante");
variable.equals("constante");
Esto, que esta bien, te esta imponiendo la precondición de "variable != null" al operar por Strings, lo que no tiene porque ser verdad u obligatorio (null != "pepe" tiene que devolver false, no una NullPointerException).
Si hacemos en cambio
"contante".equals(variable);
no tendremos ese problema, y siempre obtendremos el valor esperado (true|false)
Enviado por Al en noviembre 04, 2005 a las 10:07 AM COT #
Un tema muy importante con String es que utilizan 2 bytes para cada caracter, por lo que en aplicaciones que no requieran el juego de caracteres extendido, estan utilizando el doble. Es un error comun utilizar String para claves Hash cuando lo mejor seria utilizar Array de Bytes.
Solo por dejar mi semilla
Enviado por troll en enero 23, 2006 a las 10:51 AM COT #
como comparar dos stringbuffer
Enviado por luis sintes estevez en marzo 20, 2008 a las 11:25 AM COT #
saludos antes que nada, solo para dar las gracias por dedicarle tiempo, me ayudaste a dejar mas claro lo de los objetos String.
Enviado por Israel en junio 17, 2008 a las 08:37 PM COT #