Como o Java faz a concatenação de strings usando “+”?

Eu li sobre o modo como o Java trabalha com o operador += , usando StringBuilder .
É o mesmo com uma operação ("a" + "b") ?

Não. Não é o mesmo usando StringBuilder que fazer "a" + "b" .

Em Java, as instâncias String são imutáveis.

Então, se você fizer:

 String c = "a" + "b"; 

Você está criando novas strings toda vez que você concatena.

Por outro lado, StringBuilder é como um buffer que pode crescer conforme necessário ao append novas Strings.

 StringBuilder c = new StringBuilder(); c.append("a"); c.append("b"); // c is only created once and appended "a" and "b". 

Regra do polegar é (alterado graças aos comentários que recebi):

Se você for concatenar muito (ou seja, concatenar dentro de um loop ou gerar um grande XML formado por diversas variables ​​concatenadas de strings), use StringBuilder. Caso contrário, a concatenação simples (usando o operador +) ficará bem.

As otimizações do compilador também desempenham um papel enorme ao compilar esse tipo de código.

Aqui está mais uma explicação sobre o tópico.

E mais perguntas do StackOVerflow sobre o problema:

É melhor reutilizar um StringBuilder em um loop?

Qual é a melhor maneira de construir uma string de itens delimitados em Java?

Concatenação StringBuilder vs String em toString () em Java

Se você combinar strings literais (literalmente "foo" + "bar" ), o compilador faz isso em tempo de compilation, não em tempo de execução.

Se você tem duas strings não-literais e se junta a elas com + , o compilador (da Sun, de qualquer forma) usará um StringBuilder sob as capas, mas não necessariamente da maneira mais eficiente. Então, por exemplo, se você tem isso:

 String repeat(String a, int count) { String rv; if (count < = 0) { return ""; } rv = a; while (--count > 0) { rv += a; } return rv; } 

… o que o compilador Sun irá realmente produzir como bytecode é algo como isto:

 String repeat(String a, int count) { String rv; if (count < = 0) { return ""; } rv = a; while (--count > 0) { rv = new StringBuilder().append(rv).append(a).toString(); } return rv; } 

(Sim, realmente – veja a desassembly no final desta resposta.) Observe que ele criou um novo StringBuilder em cada iteração e depois converteu o resultado em String . Isso é ineficiente (mas não importa a menos que você esteja fazendo muito ) por causa de todas as alocações de memory temporária: ele aloca um StringBuilder e seu buffer, possivelmente realoca o buffer no primeiro append [se rv é mais de 16 caracteres, que é o tamanho padrão do buffer] e se não for o primeiro, então quase certamente no segundo append , então aloca uma String no final – e então faz tudo novamente na próxima iteração.

Você poderia ganhar eficiência, se necessário, reescrevendo-o para usar explicitamente um StringBuilder :

 String repeat(String a, int count) { StringBuilder rv; if (count < = 0) { return ""; } rv = new StringBuilder(a.length() * count); while (count-- > 0) { rv.append(a); } return rv.toString(); } 

Lá, usamos um StringBuilder explícito e também definimos sua capacidade inicial de buffer para ser grande o suficiente para manter o resultado. Isso é mais eficiente em termos de memory, mas é claro, menos claro para os mantenedores de código inexperientes e um pouco mais para escrever. Portanto, se você encontrar um problema de desempenho com um loop restrito de concatenação de string, isso pode ser uma maneira de resolvê-lo.

Você pode ver isso sob o StringBuilder em ação com a seguinte class de teste:

 public class SBTest { public static final void main(String[] params) { System.out.println(new SBTest().repeat("testing ", 4)); System.exit(0); } String repeat(String a, int count) { String rv; if (count < = 0) { return ""; } rv = a; while (--count > 0) { rv += a; } return rv; } } 

… que desmonta (usando o javap -c SBTest ) assim:

 Compiled from "SBTest.java" public class SBTest extends java.lang.Object{ public SBTest(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."":()V 4: return public static final void main(java.lang.String[]); Code: 0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 3: new #3; //class SBTest 6: dup 7: invokespecial #4; //Method "":()V 10: ldc #5; //String testing 12: iconst_4 13: invokevirtual #6; //Method repeat:(Ljava/lang/String;I)Ljava/lang/String; 16: invokevirtual #7; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 19: iconst_0 20: invokestatic #8; //Method java/lang/System.exit:(I)V 23: return java.lang.String repeat(java.lang.String, int); Code: 0: iload_2 1: ifgt 7 4: ldc #9; //String 6: areturn 7: aload_1 8: astore_3 9: iinc 2, -1 12: iload_2 13: ifle 38 16: new #10; //class java/lang/StringBuilder 19: dup 20: invokespecial #11; //Method java/lang/StringBuilder."":()V 23: aload_3 24: invokevirtual #12; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 27: aload_1 28: invokevirtual #12; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 31: invokevirtual #13; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 34: astore_3 35: goto 9 38: aload_3 39: areturn } 

Observe como um novo StringBuilder é criado em cada iteração do loop e criado usando a capacidade padrão do buffer.

Todo esse material de alocação temporária soa feio, mas, novamente, somente se você estiver lidando com loops substanciais e / ou strings substanciais. Além disso, quando o bytecode resultante é executado, a JVM pode otimizá-lo ainda mais. A JVM HotSpot da Sun, por exemplo, é um compilador de otimização JIT muito maduro. Uma vez identificado o loop como um hot spot, ele pode encontrar uma maneira de refatorá-lo. Ou não, claro. 🙂

Minha regra geral é que eu me preocupo com isso quando vejo um problema de desempenho, ou se eu sei que estou fazendo muita concatenação e é muito provável que seja um problema de desempenho e o código não será impactado significativamente do ponto de vista de manutenção se eu usar um StringBuilder vez disso. A liga raivosa de otimização anti-prematura provavelmente discordaria de mim no segundo deles. 🙂

Sim, é o mesmo, mas o compilador também pode otimizar as concatenações de literais antes de emitir o código, portanto, "a"+"b" pode ser emitido como "ab" diretamente.

Para concatenar um número fixo de strings em uma expressão com + , o compilador produzirá código usando um único StringBuilder .

Por exemplo, a linha

 String d = a + b + c; 

resulta no mesmo bytecode que a linha

 String d = new StringBuilder().append(a).append(b).append(c).toString(); 

quando compilado usando o compilador javac. (O compilador do Eclipse produz um código um pouco mais otimizado chamando o new StringBuilder(a) , salvando assim uma chamada de método.)

Como mencionado em outras respostas, o compilador irá concatenar literais de string como "a" + "b" em uma única string, produzindo bytecode que contém "ab" .

Como mencionado em todos os lugares na rede, você não deve usar + para criar uma string dentro de um loop , porque você está copiando o início da string repetidamente para novas strings. Nesta situação você deve usar um StringBuilder que você declara fora do loop.

"a" + "b" operação

Embora legível, fácil de formatar e direto, concatenar strings com “+” é considerado ruim em Java.

Cada vez que você acrescenta algo via ‘+’ (String.concat ()) uma nova String é criada, o conteúdo antigo da String é copiado, o novo conteúdo é acrescentado e a antiga String é descartada. Quanto maior o String recebe mais tempo – há mais para copiar e mais lixo é produzido. Nota: se você está apenas concatenando algumas (digamos, 3,4) strings e não construindo uma string através de um loop ou apenas escrevendo algum aplicativo de teste, você ainda pode ficar com “+”

Usando o StringBuilder

Ao executar uma manipulação extensiva de String (ou append por meio de um loop), é recomendável replace “+” por StringBuilder .append. Os objects intermediários mencionados no caso de “+” não são criados durante a chamada do método append() .

Também deve ser notado que as otimizações no compilador Sun Java, que cria automaticamente StringBuilders ( StringBuffers <5.0) quando ele vê concatenações de String. Mas isso é apenas o compilador Sun Java.

As strings são mais comumente concatenadas com o operador +, como em "Hello," + " world" + "!"

Fonte