Warning: array_keys() [function.array-keys]: The first argument should be an array in /home/viricmin/labs/wp-content/plugins/wp-syntax/geshi/geshi.php on line 2026

Warning: Invalid argument supplied for foreach() in /home/viricmin/labs/wp-content/plugins/wp-syntax/geshi/geshi.php on line 2026

Warning: Invalid argument supplied for foreach() in /home/viricmin/labs/wp-content/plugins/wp-syntax/geshi/geshi.php on line 2398

Warning: implode() [function.implode]: Argument must be an array in /home/viricmin/labs/wp-content/plugins/wp-syntax/geshi/geshi.php on line 3351

Warning: array_keys() [function.array-keys]: The first argument should be an array in /home/viricmin/labs/wp-content/plugins/wp-syntax/geshi/geshi.php on line 3374

Warning: Invalid argument supplied for foreach() in /home/viricmin/labs/wp-content/plugins/wp-syntax/geshi/geshi.php on line 3374

Warning: Invalid argument supplied for foreach() in /home/viricmin/labs/wp-content/plugins/wp-syntax/geshi/geshi.php on line 3415

Warning: array_keys() [function.array-keys]: The first argument should be an array in /home/viricmin/labs/wp-content/plugins/wp-syntax/geshi/geshi.php on line 3467

Warning: Invalid argument supplied for foreach() in /home/viricmin/labs/wp-content/plugins/wp-syntax/geshi/geshi.php on line 3467

Warning: array_keys() [function.array-keys]: The first argument should be an array in /home/viricmin/labs/wp-content/plugins/wp-syntax/geshi/geshi.php on line 3612

Warning: Invalid argument supplied for foreach() in /home/viricmin/labs/wp-content/plugins/wp-syntax/geshi/geshi.php on line 3612

Actualmente trabajo de becario (programador) modificando algunos programas escritos en Java (que funcionarán sobre OpenJDK) y debido a las tareas que me encargan me acabé preguntando sobre el rendimiento en el tratamiento de cadenas. Buscando por internet encontré este artículo [1] que aclaró bastante las cosas. Yo lo voy a reescribir aquí por si algún día desaparece ese artículo, además voy a fijarme en algún otro aspecto no comentado allí.

En Java podemos encontrar tres clases diferentes que nos permiten trabajar con cadenas, la archiconocida String, StringBuffer y StringBuilder. Debido a que los objetos String son inmutables, cada vez que se hace una concatenación con el operador + se debe crear un nuevo objeto que albergue el resultado (y en caso de que las cadenas anteriores no esten referenciadas, eliminar los objetos correspondientes). Por eso también existe la clase StringBuffer que nos permite añadir a la cadena que contiene otras cadenas con su método append sin tener que construir ni destruir ningún objeto. Con ello se consigue una mejora de rendimiento considerable cuando se hacen muchas concatenaciones, luego si se quiere trabajar con un objeto String solo tenemos que usar el método toString y listos.

La clase StringBuffer está diseñada para ser segura en un entorno concurrente, por lo que tiene código extra para gestionar los bloqueos y demás. Esta característica no siempre nos es útil y puede hacer decrecer el rendimiento en demasía, por lo que también se creó la clase StringBuilder que no es segura para operaciones concurrentes pero puede ser el doble de rápida que StringBuffer (además, casi siempre que trabajamos con cadenas es de forma no concurrente).

Tanto StringBuffer como StringBuilder escalan linealmente a medida que aumentamos el número de concatenaciones, no siendo así en el caso de la clase String que parece mostrar un incremento supralineal en el consumo de recursos. Aquí os dejo las tablas de resultados, para String no están completas porque tardaba demasiado y me he cansado.

run:
StringBuffer (100): 0
StringBuffer (1000): 3
StringBuffer (10000): 19
StringBuffer (100000): 13
StringBuffer (1000000): 75
StringBuffer (10000000): 642
StringBuffer (50000000): 2738
 
StringBuilder (100): 0
StringBuilder (1000): 0
StringBuilder (10000): 4
StringBuilder (100000): 70
StringBuilder (1000000): 32
StringBuilder (10000000): 342
StringBuilder (50000000): 1706
 
String (100): 1
String (1000): 67
String (10000): 777
String (100000): 80040

Los números indican la cantidad de milisegundos consumidos para la cantidad indicada de concatenaciones. El código que he usado es éste:

public static void main(String[] args) {
        // StringBuffer
        System.out.println("StringBuffer (100): "+cSBuffer(100));
        System.out.println("StringBuffer (1000): "+cSBuffer(1000));
        System.out.println("StringBuffer (10000): "+cSBuffer(10000));
        System.out.println("StringBuffer (100000): "+cSBuffer(100000));
        System.out.println("StringBuffer (1000000): "+cSBuffer(1000000));
        System.out.println("StringBuffer (10000000): "+cSBuffer(10000000));
        System.out.println("StringBuffer (50000000): "+cSBuffer(50000000));
        // END StringBuffer
 
        System.out.println();
 
        // StringBuilder
        System.out.println("StringBuilder (100): "+cSBuilder(100));
        System.out.println("StringBuilder (1000): "+cSBuilder(1000));
        System.out.println("StringBuilder (10000): "+cSBuilder(10000));
        System.out.println("StringBuilder (100000): "+cSBuilder(100000));
        System.out.println("StringBuilder (1000000): "+cSBuilder(1000000));
        System.out.println("StringBuilder (10000000): "+cSBuilder(10000000));
        System.out.println("StringBuilder (50000000): "+cSBuilder(50000000));
        // END StringBuilder
 
        System.out.println();
 
        // StringBuilder
        System.out.println("String (100): "+cS(100));
        System.out.println("String (1000): "+cS(1000));
        System.out.println("String (10000): "+cS(10000));
        System.out.println("String (100000): "+cS(100000));
        System.out.println("String (1000000): "+cS(1000000));
        System.out.println("String (10000000): "+cS(10000000));
        System.out.println("String (50000000): "+cS(50000000));
        // END StringBuilder
    }
 
    static private long cSBuffer(long num)
    {
        long ini = System.currentTimeMillis();
 
        StringBuffer sbuffer = new StringBuffer();
 
        for(int i=0; i

Por cierto, y esto tiene poco que ver con lo de las clases. Lo he probado con IKVM también... y bueno, tengo que decir que va el doble de lento que con OpenJDK (no lo he probado con la máquina Hotspot de Sun).

Referencias

  1. http://www.dosideas.com/java/339-string-vs-stringbuffer-vs-stringbuilder.html