Java Language El operador de concatenación de cuerdas (+)


Ejemplo

El símbolo + puede significar tres operadores distintos en Java:

  • Si no hay ningún operando antes del + , entonces es el operador Unario Plus.
  • Si hay dos operandos, y ambos son numéricos. entonces es el operador binario de adición.
  • Si hay dos operandos, y al menos uno de ellos es un String , entonces es el operador binario de concatenación.

En el caso simple, el operador de concatenación une dos cadenas para dar una tercera cadena. Por ejemplo:

String s1 = "a String";
String s2 = "This is " + s1;    // s2 contains "This is a String"

Cuando uno de los dos operandos no es una cadena, se convierte en una String siguiente manera:

  • Un operando cuyo tipo es un tipo primitivo se convierte como si llamando toString() en el valor en caja.

  • Un operando cuyo tipo es un tipo de referencia se convierte llamando al método toString() del operando. Si el operando es null , o si el método toString() devuelve un null , en su lugar se usa el literal de cadena "null" .

Por ejemplo:

int one = 1;
String s3 = "One is "  + one;         // s3 contains "One is 1"
String s4 = null + " is null";        // s4 contains "null is null"
String s5 = "{1} is " + new int[]{1}; // s5 contains something like
                                      // "{} is [I@xxxxxxxx"

La explicación para el ejemplo de s5 es que el método toString() en tipos de matriz se hereda de java.lang.Object , y el comportamiento es producir una cadena que consiste en el nombre del tipo y el código de identidad del objeto.

El operador de concatenación se especifica para crear un nuevo objeto de String , excepto en el caso de que la expresión sea una expresión constante. En el último caso, la expresión se evalúa en el tipo de compilación, y su valor de tiempo de ejecución es equivalente a una cadena literal. Esto significa que no hay una sobrecarga de tiempo de ejecución al dividir un literal de cadena larga como este:

String typing = "The quick brown fox " +
                "jumped over the " +
                "lazy dog";           // constant expression

Optimización y eficiencia.

Como se señaló anteriormente, con la excepción de las expresiones constantes, cada expresión de concatenación de cadena crea un nuevo objeto de String . Considere este código:

public String stars(int count) {
    String res = "";
    for (int i = 0; i < count; i++) {
        res = res + "*";
    }
    return res;
}

En el método anterior, cada iteración del bucle creará una nueva String que es un carácter más largo que la iteración anterior. Cada concatenación copia todos los caracteres en las cadenas de operandos para formar la nueva String . Así, las stars(N) :

  • crea N nuevos objetos String , y tira todos menos el último,
  • copiar N * (N + 1) / 2 caracteres, y
  • generar O(N^2) bytes de basura.

Esto es muy caro para N grande. De hecho, cualquier código que concatene cadenas en un bucle puede tener este problema. Una mejor manera de escribir esto sería como sigue:

public String stars(int count) {
    // Create a string builder with capacity 'count' 
    StringBuilder sb = new StringBuilder(count);
    for (int i = 0; i < count; i++) {
        sb.append("*");
    }
    return sb.toString();
}

Idealmente, debería ajustar la capacidad de la StringBuilder , pero si esto no es práctico, la clase va a crecer de forma automática la matriz respaldo que el constructor utiliza para contener caracteres. (Nota: la implementación expande la matriz de respaldo de manera exponencial. Esta estrategia mantiene esa cantidad de caracteres que se copian en O(N) lugar de O(N^2) .

Algunas personas aplican este patrón a todas las concatenaciones de cuerdas. Sin embargo, esto no es necesario porque JLS permite que un compilador de Java optimice las concatenaciones de cadenas dentro de una sola expresión. Por ejemplo:

String s1 = ...;
String s2 = ...;    
String test = "Hello " + s1 + ". Welcome to " + s2 + "\n";

será típicamente optimizado por el compilador bytecode a algo como esto;

StringBuilder tmp = new StringBuilder();
tmp.append("Hello ")
tmp.append(s1 == null ? "null" + s1);
tmp.append("Welcome to ");
tmp.append(s2 == null ? "null" + s2);
tmp.append("\n");
String test = tmp.toString();

(El compilador JIT puede optimizar eso aún más si puede deducir que s1 o s2 no puede ser null ). Pero tenga en cuenta que esta optimización solo está permitida dentro de una sola expresión.

En resumen, si le preocupa la eficacia de las concatenaciones de cadenas:

  • Optimice a mano si está haciendo concatenación repetida en un bucle (o similar).
  • No optimice manualmente una sola expresión de concatenación.