La clase String es una clase no modificable. Esto quiere decir que cuando se modifica un String se crea un
nuevo objeto String modificado a partir del original y el recolector de basura
es el encargado de eliminar de la memoria el String original.
Java proporciona la clase
StringBuffer y a partir de Java 5 la clase StringBuilder para trabajar con
cadenas de caracteres sobre las que vamos a realizar modificaciones frecuentes
de su contenido.
La diferencia
entre StringBuffer y StringBuilder es que los métodos de StringBuffer están sincronizados y los de
StringBuilder no lo están. Por este motivo StringBuilder ofrece mejor rendimiento que StringBuffer y la utilizaremos cuando la aplicación tenga un solo hilo de
ejecución.
En general decidiremos cuando
usar String, StringBuilder o StringBuffer según lo siguiente:
-
Usaremos String
si la cadena de caracteres no va a cambiar.
-
Usaremos StringBuilder si la cadena de caracteres
puede cambiar y solamente tenemos un hilo de ejecución.
-
Usaremos StringBuffer si la cadena de caracteres puede
cambiar y tenemos varios hilos de ejecución.
En esta entrada utilizaremos StringBuilder
teniendo en cuenta que todo lo que se explica aquí es aplicable a StringBuffer.
Constructores de la Clase StringBuilder
Un objeto de tipo StringBuilder gestiona
automáticamente su capacidad
– Se crea con una capacidad inicial.
– La capacidad se incrementa
cuando es necesario.
La clase StringBuilder
proporcionan varios constructores, algunos de ellos son:
CONSTRUCTOR
|
DESCRIPCIÓN
|
StringBuilder ()
|
Crea un StringBuilder
vacío.
StringBuilder sb = new StringBuilder ();
|
StringBuilder(int n)
|
Crea un StringBuilder
vacío con capacidad para n caracteres.
|
StringBuilder(String s);
|
Crea un StringBuilder y le asigna el
contenido del String s.
String
s = "ejemplo";
StringBuilder sb = new StringBuilder (s);
|
Métodos de la Clase StringBuilder
La clase StringBuilder proporcionan métodos para acceder y
modificar la cadena de caracteres. Algunos de ellos son:
MÉTODO
|
DESCRIPCIÓN
|
length()
|
Devuelve
la longitud de la cadena
|
append(X);
|
Añade
X al final de la cadena. X puede ser de cualquier tipo
|
insert(posicion, X)
|
Inserta
X en la posición indicada. X puede ser de cualquier tipo.
|
setCharAt(posicion, c)
|
Cambia
el carácter que se encuentra en la posición indicada, por el carácter c.
|
charAt(posicion)
|
Devuelve
el carácter que se encuentra en la posición indicada.
|
indexOf(‘caracter’)
|
Devuelve
la posición de la primera aparición de carácter. Devuelve -1 si no lo encuentra.
|
lastIndexOf(‘caracter’)
|
Devuelve
la posición de la última aparición de carácter.
Devuelve -1 si no lo encuentra. |
substring(n1,n2)
|
Devuelve
la subcadena (String) comprendida entre las posiciones n1 y n2 - 1. Si no se especifica n2, devuelve desde n1 hasta el final.
|
delete(inicio, fin)
|
Elimina
los caracteres desde la posición inicio hasta fin - 1.
|
reverse()
|
Invierte
el contenido de la cadena
|
toString()
|
Devuelve
el String equivalente.
|
Los
puedes consultar todos en la API
de Java:
http://docs.oracle.com/javase/10/docs/api/java/lang/StringBuilder.htmlhttp://docs.oracle.com/javase/10/docs/api/java/lang/StringBuffer.html
Ejemplo de uso de la clase StringBuilder:
Vamos a escribir un método separarMiles que reciba un String que
representa un número entero y devuelva un String con el mismo número al que se
le añadirán los puntos separadores de millares.
Por ejemplo, si el método
recibe el String "12345678" debe devolver el String "12.345.678"
Este problema lo podemos
resolver de varias formas. En este caso la idea es darle la vuelta al número e
insertar el primer punto en la cuarta posición del String, el siguiente punto 4
posiciones más adelante el siguiente otras 4 posiciones más adelante .... hasta
llegar al final del número. De esta forma obtendremos grupos de 3 cifras
separados por punto.
Finalmente le volvemos a dar
la vuelta y ya lo tendremos.
Por ejemplo si el String es:
"12345678"
Primero le damos la vuelta:
"87654321"
Ahora tenemos que insertar un
punto donde está el 5. Nos queda:
"876.54321"
Insertamos otro punto cuatro
posiciones más adelante, donde está el 2:
"876.543.21"
Ahora intentaríamos insertar
otro punto cuatro posiciones más adelante pero como llegamos al final el
proceso termina.
Si le damos la vuelta
obtendremos el resultado:
"12.345.678"
/*
* Ejemplo de uso de StringBuilder
* Separador de millares
*/
package string8;
public class String8 {
public static void main(String[] args) {
String s = "1234567890";
s = separarMiles(s);
System.out.println(s);
}
public static String separarMiles(String s){
//creamos un StringBuilder a partir del String s
StringBuilder aux = new StringBuilder(s);
//le damos la vuelta
aux.reverse();
//variable que indica donde insertar el siguiente punto
int posicion = 3;
//mientras no lleguemos al final del número
while(posicion < aux.length()){
//insertamos un punto en la posición
aux.insert(posicion,'.');
//siguiente posición donde insertar
posicion+=4;
}
//le damos de nuevo la vuelta
aux.reverse();
//el StringBuilder se pasa a String y se devuelve
return aux.toString();
}
}
Eficiencia de la Clase StringBuilder frente a la Clase String
Podemos comprobar que es más eficiente utilizar StringBuilder frente a String realizando la siguiente prueba:
Vamos a concatenar un número grande de cadenas de
caracteres, por ejemplo 100000, y vamos a medir el tiempo que se emplea en
hacerlo.
Lo vamos a realizar primero utilizando la clase String. A
continuación utilizando la clase StringBuilder y finalmente lo vamos a hacer
utilizando StringBuilder pero asignando inicialmente memoria para la longitud
final de la cadena resultante.
public class String3 {
public static void main(String[] args) {
String s = "cadena";
long t1, t2;
int n = 100000;
System.out.print("Concatenar " + n + " cadenas con String: ");
t1 = System.currentTimeMillis();
concatenar(s,n);
t2 = System.currentTimeMillis();
System.out.println((t2-t1) + " milisegundos");
System.out.print("Concatenar " + n + " cadenas con StringBuilder: ");
t1 = System.currentTimeMillis();
concatenar1(s,n);
t2 = System.currentTimeMillis();
System.out.println((t2-t1) + " milisegundos");
System.out.print("Concatenar " + n + " cadenas con StringBuilder Optimizado: ");
t1 = System.currentTimeMillis();
concatenar2(s,n);
t2 = System.currentTimeMillis();
System.out.println((t2-t1) + " milisegundos");
}
//método que concatena n cadenas usando la clase String
public static String concatenar(String s, int n) {
String resultado = s;
for (int i = 1; i < n; i++) {
resultado = resultado + s;
}
return resultado;
}
//método que concatena n cadenas usando la clase StringBuilder
public static String concatenar1(String s, int n) {
StringBuilder resultado = new StringBuilder(s);
for (int i = 1; i < n; i++) {
resultado.append(s);
}
return resultado.toString();
}
//método optimizado que concatena n cadenas usando la clase StringBuilder
//se crea un StringBuilder inicial con el tamaño total del String resultante
public static String concatenar2(String s, int n) {
StringBuilder resultado = new StringBuilder(s.length() * n);
for (int i = 0; i < n; i++) {
resultado.append(s);
}
return resultado.toString();
}
}
La salida del programa dependerá del ordenador que
utilicemos. En cualquier caso nos debe mostrar que concatenar utilizando String
es más lento que si utilizamos StringBuilder.
En mi caso el resultado obtenido ha sido este:
En mi caso el resultado obtenido ha sido este:
Concatenar 100000 cadenas con String: 304435 milisegundos
Concatenar 100000 cadenas con StringBuilder: 15 milisegundos
Concatenar 100000 cadenas con StringBuilder Optimizado: 1 milisegundos
muy bien explicado!
ResponderEliminarExcelente la explicación amigo. Sin embargo el ejercicio de eficiencia considero que no está bien planteado, puesto que ejecutas el caso más malo al principio y el que esperas sea más eficiente al final; sabes que si se repite una prueba, la misma será más rápida a la segunda iteración, y a la tercera será más rápida, y así sucesivamente. Además, utilizas como ejemplo de concatenar Strings el operador (+), el cual no está recomendado por su ineficiencia; para eso la clase String tiene el método concat(), el cual es casi tan eficiente como la clase StringBuilder. Te dejo la salida de mi programa, el cual hice multihilo, para que los resultados de una prueba no incidan en los resultados de las demás:
ResponderEliminar[3] Concatenando 1000000 cadenas (Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse porta elementum efficitur.) con StringBuilder y su metodo append().
Fin del main...
[2] Concatenando 1000000 cadenas (Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse porta elementum efficitur.) con String y su método concat().
[4] Concatenando 1000000 cadenas (Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse porta elementum efficitur.) con StringBuilder con tamaño reservado en memoria.
[4] Completado en 141 milisegundos
[3] Completado en 1338 milisegundos
[2] Completado en 1521 milisegundos
Por el número delantero sabes a cuál hilo pertenece el resultado.
He realizado varias pruebas y a veces el método de String.concat() es tan rápido como hacerlo con StringBuilder sin reservar memoria:
alvaro@alvaro-ProBook:~/workspace/StringsConcatEfficiency/bin$ java -Xms960m -Xmx960m -Xss256k com.aju.efficiency.strings.StringsMain
[2] Concatenando 1000000 cadenas (Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse porta elementum efficitur.) con String y su método concat().
Fin del main...
[3] Concatenando 1000000 cadenas (Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse porta elementum efficitur.) con StringBuilder y su metodo append().
[4] Concatenando 1000000 cadenas (Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse porta elementum efficitur.) con StringBuilder con tamaño reservado en memoria.
[4] Completado en 243 milisegundos
[3] Completado en 1337 milisegundos
[2] Completado en 1338 milisegundos
alvaro@alvaro-ProBook:~/workspace/StringsConcatEfficiency/bin$
El código lo puedes encontrar en https://bitbucket.org/aurbaez/stringsconcatefficiency
Saludos...
A mi parecer el StringBuilder supera al momento de concatenar al método de concat de la clase String, lo eh probado en un ejercicio https://projecteuler.net/problem=40 . Proyecto euler :D
EliminarMuy buen post, pero si no estoy mal el meto substring(a,b); crea la sub-cadena desde el indice a (incluido) hasta el b(no incluido)
ResponderEliminarTienes toda la razón. Ya está corregido en la entrada.
EliminarUn saludo y gracias por avisar del error.
Quién plagia a quien ?
ResponderEliminarhttp://aquiseprograma.co/2015/11/como-concatenar-correctamente-en-java/
en realidad con las pcs de hoy dia... 304435 mseg (aprox. 5seg) no es en extremo significativo como para alterar los tiempos de respuesta de una app.
ResponderEliminarSi es un servidor que ejecuta miles de peticiones por segundo empieza a echar cuentas
Eliminar