Pergunta

Assumindo as strings a e b:

a += b
a = a.concat(b)

Sob o capô, eles são a mesma coisa?

Aqui está o concat descompilado como referência.Eu gostaria de poder descompilar o + operador também para ver o que isso faz.

public String concat(String s) {

    int i = s.length();
    if (i == 0) {
        return this;
    }
    else {
        char ac[] = new char[count + i];
        getChars(0, count, ac, 0);
        s.getChars(0, i, ac, count);
        return new String(0, count + i, ac);
    }
}
Foi útil?

Solução

Não, não exatamente.

Em primeiro lugar, há uma ligeira diferença na semântica.Se a é null, então a.concat(b) lança um NullPointerException mas a+=b tratará o valor original de a Como se fosse null.Além disso, o concat() método aceita apenas String valores enquanto o + operador converterá silenciosamente o argumento em uma String (usando o toString() método para objetos).Então o concat() método é mais rigoroso no que aceita.

Para dar uma olhada nos bastidores, escreva uma aula simples com a += b;

public class Concat {
    String cat(String a, String b) {
        a += b;
        return a;
    }
}

Agora desmonte com javap -c (incluído no Sun JDK).Você deverá ver uma listagem incluindo:

java.lang.String cat(java.lang.String, java.lang.String);
  Code:
   0:   new     #2; //class java/lang/StringBuilder
   3:   dup
   4:   invokespecial   #3; //Method java/lang/StringBuilder."<init>":()V
   7:   aload_1
   8:   invokevirtual   #4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   11:  aload_2
   12:  invokevirtual   #4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   15:  invokevirtual   #5; //Method java/lang/StringBuilder.toString:()Ljava/lang/    String;
   18:  astore_1
   19:  aload_1
   20:  areturn

Então, a += b é o equivalente a

a = new StringBuilder()
    .append(a)
    .append(b)
    .toString();

O concat método deve ser mais rápido.No entanto, com mais cordas o StringBuilder método vence, pelo menos em termos de desempenho.

O código fonte de String e StringBuilder (e sua classe base package-private) está disponível em src.zip do Sun JDK.Você pode ver que está construindo um array de caracteres (redimensionando conforme necessário) e depois jogando-o fora ao criar o array final. String.Na prática, a alocação de memória é surpreendentemente rápida.

Atualizar: Como observa Pawel Adamski, o desempenho mudou no HotSpot mais recente. javac ainda produz exatamente o mesmo código, mas o compilador de bytecode trapaceia.O teste simples falha totalmente porque todo o corpo do código é jogado fora.Resumindo System.identityHashCode (não String.hashCode) mostra o StringBuffer o código tem uma ligeira vantagem.Sujeito a alterações quando a próxima atualização for lançada ou se você usar uma JVM diferente.De @lukaseder, uma lista de intrínsecos do HotSpot JVM.

Outras dicas

Niyaz está correto, mas também vale a pena notar que o operador especial + pode ser convertido em algo mais eficiente pelo compilador Java.Java tem uma classe StringBuilder que representa uma String mutável e não segura para thread.Ao realizar várias concatenações de String, o compilador Java converte silenciosamente

String a = b + c + d;

em

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

o que para strings grandes é significativamente mais eficiente.Pelo que eu sei, isso não acontece quando você usa o método concat.

No entanto, o método concat é mais eficiente ao concatenar uma String vazia em uma String existente.Neste caso, a JVM não precisa criar um novo objeto String e pode simplesmente retornar o existente.Ver a documentação do concat para confirmar isso.

Portanto, se você está muito preocupado com a eficiência, deve usar o método concat ao concatenar Strings possivelmente vazias e usar + caso contrário.No entanto, a diferença de desempenho deve ser insignificante e você provavelmente não deveria se preocupar com isso.

Fiz um teste semelhante ao @marcio, mas com o seguinte loop:

String c = a;
for (long i = 0; i < 100000L; i++) {
    c = c.concat(b); // make sure javac cannot skip the loop
    // using c += b for the alternative
}

Só para garantir, eu joguei StringBuilder.append() também.Cada teste foi executado 10 vezes, com 100 mil repetições para cada corrida.Aqui estão os resultados:

  • StringBuilder vence sem dúvida.O resultado do tempo do relógio foi 0 para a maioria das corridas, e a mais longa durou 16ms.
  • a += b leva cerca de 40.000 ms (40s) para cada execução.
  • concat requer apenas 10.000 ms (10 s) por execução.

Ainda não descompilei a classe para ver os detalhes internos ou executá-la no profiler, mas suspeito a += b passa grande parte do tempo criando novos objetos de StringBuilder e depois convertê-los de volta para String.

A maioria das respostas aqui são de 2008.Parece que as coisas mudaram ao longo do tempo.Meus últimos benchmarks feitos com JMH mostram que no Java 8 + é cerca de duas vezes mais rápido que concat.

Minha referência:

@Warmup(iterations = 5, time = 200, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 5, time = 200, timeUnit = TimeUnit.MILLISECONDS)
public class StringConcatenation {

    @org.openjdk.jmh.annotations.State(Scope.Thread)
    public static class State2 {
        public String a = "abc";
        public String b = "xyz";
    }

    @org.openjdk.jmh.annotations.State(Scope.Thread)
    public static class State3 {
        public String a = "abc";
        public String b = "xyz";
        public String c = "123";
    }


    @org.openjdk.jmh.annotations.State(Scope.Thread)
    public static class State4 {
        public String a = "abc";
        public String b = "xyz";
        public String c = "123";
        public String d = "!@#";
    }

    @Benchmark
    public void plus_2(State2 state, Blackhole blackhole) {
        blackhole.consume(state.a+state.b);
    }

    @Benchmark
    public void plus_3(State3 state, Blackhole blackhole) {
        blackhole.consume(state.a+state.b+state.c);
    }

    @Benchmark
    public void plus_4(State4 state, Blackhole blackhole) {
        blackhole.consume(state.a+state.b+state.c+state.d);
    }

    @Benchmark
    public void stringbuilder_2(State2 state, Blackhole blackhole) {
        blackhole.consume(new StringBuilder().append(state.a).append(state.b).toString());
    }

    @Benchmark
    public void stringbuilder_3(State3 state, Blackhole blackhole) {
        blackhole.consume(new StringBuilder().append(state.a).append(state.b).append(state.c).toString());
    }

    @Benchmark
    public void stringbuilder_4(State4 state, Blackhole blackhole) {
        blackhole.consume(new StringBuilder().append(state.a).append(state.b).append(state.c).append(state.d).toString());
    }

    @Benchmark
    public void concat_2(State2 state, Blackhole blackhole) {
        blackhole.consume(state.a.concat(state.b));
    }

    @Benchmark
    public void concat_3(State3 state, Blackhole blackhole) {
        blackhole.consume(state.a.concat(state.b.concat(state.c)));
    }


    @Benchmark
    public void concat_4(State4 state, Blackhole blackhole) {
        blackhole.consume(state.a.concat(state.b.concat(state.c.concat(state.d))));
    }
}

Resultados:

Benchmark                             Mode  Cnt         Score         Error  Units
StringConcatenation.concat_2         thrpt   50  24908871.258 ± 1011269.986  ops/s
StringConcatenation.concat_3         thrpt   50  14228193.918 ±  466892.616  ops/s
StringConcatenation.concat_4         thrpt   50   9845069.776 ±  350532.591  ops/s
StringConcatenation.plus_2           thrpt   50  38999662.292 ± 8107397.316  ops/s
StringConcatenation.plus_3           thrpt   50  34985722.222 ± 5442660.250  ops/s
StringConcatenation.plus_4           thrpt   50  31910376.337 ± 2861001.162  ops/s
StringConcatenation.stringbuilder_2  thrpt   50  40472888.230 ± 9011210.632  ops/s
StringConcatenation.stringbuilder_3  thrpt   50  33902151.616 ± 5449026.680  ops/s
StringConcatenation.stringbuilder_4  thrpt   50  29220479.267 ± 3435315.681  ops/s

Tom está correto ao descrever exatamente o que o operador + faz.Isso cria um temporário StringBuilder, anexa as partes e termina com toString().

No entanto, todas as respostas até agora ignoram os efeitos das otimizações de tempo de execução do HotSpot.Especificamente, essas operações temporárias são reconhecidas como um padrão comum e substituídas por código de máquina mais eficiente em tempo de execução.

@marcio:Você criou um micro-referência;com JVMs modernas, esta não é uma forma válida de criar perfil de código.

A razão pela qual a otimização do tempo de execução é importante é que muitas dessas diferenças no código – incluindo até mesmo a criação de objetos – são completamente diferentes quando o HotSpot começa a funcionar.A única maneira de saber com certeza é traçar o perfil do seu código no local.

Finalmente, todos esses métodos são incrivelmente rápidos.Este pode ser um caso de otimização prematura.Se você tem um código que concatena muito strings, a maneira de obter velocidade máxima provavelmente não tem nada a ver com os operadores que você escolhe, mas sim com o algoritmo que você está usando!

Que tal alguns testes simples?Utilizei o código abaixo:

long start = System.currentTimeMillis();

String a = "a";

String b = "b";

for (int i = 0; i < 10000000; i++) { //ten million times
     String c = a.concat(b);
}

long end = System.currentTimeMillis();

System.out.println(end - start);
  • O "a + b" versão executada em 2500ms.
  • O a.concat(b) executado em 1200 ms.

Testado várias vezes.O concat() a execução da versão levou metade do tempo em média.

Este resultado me surpreendeu porque o concat() método sempre cria uma nova string (ele retorna um "new String(result)".É bem sabido que:

String a = new String("a") // more than 20 times slower than String a = "a"

Por que o compilador não foi capaz de otimizar a criação de strings no código "a + b", sabendo que sempre resultava na mesma string?Isso poderia evitar a criação de uma nova string.Se você não acredita na afirmação acima, teste você mesmo.

Basicamente, existem duas diferenças importantes entre + e o concat método.

  1. Se você estiver usando o concat método, então você só seria capaz de concatenar strings enquanto no caso do + operador, você também pode concatenar a string com qualquer tipo de dados.

    Por exemplo:

    String s = 10 + "Hello";
    

    Neste caso, a saída deve ser 10Olá.

    String s = "I";
    String s1 = s.concat("am").concat("good").concat("boy");
    System.out.println(s1);
    

    No caso acima você deve fornecer duas strings obrigatórias.

  2. A segunda e principal diferença entre + e concat é aquele:

    Caso 1:Suponha que eu concatene as mesmas strings com concat operador desta forma

    String s="I";
    String s1=s.concat("am").concat("good").concat("boy");
    System.out.println(s1);
    

    Neste caso, o número total de objetos criados no pool é 7 assim:

    I
    am
    good
    boy
    Iam
    Iamgood
    Iamgoodboy
    

    Caso 2:

    Agora vou concatinar as mesmas strings via + operador

    String s="I"+"am"+"good"+"boy";
    System.out.println(s);
    

    No caso acima, o número total de objetos criados é de apenas 5.

    Na verdade, quando concatinamos as strings via + operador então ele mantém uma classe StringBuffer para executar a mesma tarefa da seguinte forma: -

    StringBuffer sb = new StringBuffer("I");
    sb.append("am");
    sb.append("good");
    sb.append("boy");
    System.out.println(sb);
    

    Desta forma criará apenas cinco objetos.

Então pessoal, essas são as diferenças básicas entre + e a concat método.Aproveitar :)

Para ser mais completo, gostaria de acrescentar que a definição do operador '+' pode ser encontrada no JLS SE8 15.18.1:

Se apenas uma expressão de operando for da string do tipo, a conversão da string (§5.1.11) for realizada no outro operando para produzir uma string no tempo de execução.

O resultado da concatenação da string é uma referência a um objeto String que é a concatenação das duas seqüências de operando.Os caracteres do operando esquerdo precedem os caracteres do operando direito na string recém-criada.

O objeto String é criado recentemente (§12.5), a menos que a expressão seja uma expressão constante (§15.28).

Sobre a implementação o JLS diz o seguinte:

Uma implementação pode optar por executar conversão e concatenação em uma etapa para evitar a criação e descarte um objeto de string intermediário.Para aumentar o desempenho da concatenação repetida de string, um compilador Java pode usar a classe StringBuffer ou uma técnica semelhante para reduzir o número de objetos intermediários de string que são criados pela avaliação de uma expressão.

Para tipos primitivos, uma implementação também pode otimizar a criação de um objeto de wrapper convertendo diretamente de um tipo primitivo em uma string.

Portanto, a julgar por 'um compilador Java pode usar a classe StringBuffer ou uma técnica semelhante para reduzir', diferentes compiladores podem produzir códigos de bytes diferentes.

O + operador pode funcionar entre uma string e um valor de tipo de dados string, char, inteiro, duplo ou flutuante.Ele apenas converte o valor em sua representação de string antes da concatenação.

O operador concat só pode ser feito em e com strings.Ele verifica a compatibilidade do tipo de dados e gera um erro, se não corresponderem.

Exceto isso, o código que você forneceu faz a mesma coisa.

Eu não acho.

a.concat(b) é implementado em String e acho que a implementação não mudou muito desde as primeiras máquinas Java.O + a implementação da operação depende da versão Java e do compilador.Atualmente + é implementado usando StringBuffer para tornar a operação o mais rápida possível.Talvez no futuro isso mude.Em versões anteriores do java + a operação em Strings era muito mais lenta, pois produzia resultados intermediários.

eu acho que += é implementado usando + e igualmente otimizado.

Ao usar +, a velocidade diminui conforme o comprimento da string aumenta, mas ao usar concat a velocidade fica mais estável, e a melhor opção é usar a classe StringBuilder que tem velocidade estável para fazer isso.

Eu acho que você pode entender o porquê.Mas a melhor maneira de criar strings longas é usar StringBuilder() e append(), qualquer uma das velocidades será inaceitável.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top