Java Language The String Concatenation Operator (+)

Esempio

Il simbolo + può significare tre operatori distinti in Java:

  • Se non c'è un operando prima del + , allora è l'operatore unario Plus.
  • Se ci sono due operandi e sono entrambi numerici. quindi è l'operatore di aggiunta binaria.
  • Se ci sono due operandi e almeno uno di essi è una String , allora è l'operatore di concatenazione binaria.

Nel caso semplice, l'operatore Concatenazione unisce due stringhe per dare una terza stringa. Per esempio:

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

Quando uno dei due operandi non è una stringa, viene convertito in una String come segue:

  • Un operando il cui tipo è di tipo primitivo viene convertito come se chiamasse toString() sul valore box.

  • Un operando il cui tipo è un tipo di riferimento viene convertito chiamando il metodo toString() dell'operando. Se l'operando è null o se il metodo toString() restituisce null , viene utilizzata la stringa letterale "null" .

Per esempio:

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 spiegazione per l'esempio s5 è che il metodo toString() sui tipi di array è ereditato da java.lang.Object e il comportamento è quello di produrre una stringa che comprende il nome del tipo e l'hashcode dell'identità dell'oggetto.

L'operatore Concatenazione viene specificato per creare un nuovo oggetto String , tranne nel caso in cui l'espressione sia un'espressione costante. In quest'ultimo caso, l'espressione viene valutata al tipo di compilazione e il suo valore di runtime è equivalente a un valore letterale stringa. Ciò significa che non c'è nessun sovraccarico di runtime nel dividere un lungo letterale come questo:

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

Ottimizzazione ed efficienza

Come notato sopra, con l'eccezione delle espressioni costanti, ogni espressione di concatenazione di stringhe crea un nuovo oggetto String . Considera questo codice:

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

Nel metodo sopra, ogni iterazione del ciclo creerà una nuova String che è un carattere più lungo rispetto alla precedente iterazione. Ogni concatenazione copia tutti i caratteri nelle stringhe degli operandi per formare la nuova String . Quindi, le stars(N) :

  • crea N nuovi oggetti String , e butta via tutto tranne l'ultimo,
  • copia N * (N + 1) / 2 caratteri e
  • genera O(N^2) byte di spazzatura.

Questo è molto costoso per il grande N In effetti, qualsiasi codice che concatena stringhe in un ciclo è suscettibile di avere questo problema. Un modo migliore per scrivere questo sarebbe il seguente:

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, si dovrebbe impostare la capacità della StringBuilder , ma se questo non è pratico, la classe crescerà automaticamente la matrice supporto che il costruttore utilizza per tenere caratteri. (Nota: l'implementazione espande l'array di supporto in modo esponenziale. Questa strategia mantiene quella quantità di copia del personaggio su un O(N) piuttosto che su O(N^2) .)

Alcune persone applicano questo modello a tutte le concatenazioni di stringhe. Tuttavia, questo non è necessario perché il JLS consente a un compilatore Java di ottimizzare le concatenazioni di stringhe all'interno di una singola espressione. Per esempio:

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

sarà tipicamente ottimizzato dal compilatore bytecode per qualcosa di simile;

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();

(Il compilatore JIT può ottimizzarlo ulteriormente se può dedurre che s1 o s2 non può essere null .) Ma si noti che questa ottimizzazione è consentita solo all'interno di una singola espressione.

In breve, se sei preoccupato dell'efficienza delle concatenazioni di stringhe:

  • Ottimizza a mano se esegui una concatenazione ripetuta in un ciclo (o simile).
  • Non ottimizzare a mano una singola espressione di concatenazione.