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

925

Dadas as 2 toString()implementações abaixo, qual é a preferida:

public String toString(){
    return "{a:"+ a + ", b:" + b + ", c: " + c +"}";
}

ou

public String toString(){
    StringBuilder sb = new StringBuilder(100);
    return sb.append("{a:").append(a)
          .append(", b:").append(b)
          .append(", c:").append(c)
          .append("}")
          .toString();
}

?

Mais importante, como temos apenas três propriedades, isso pode não fazer a diferença, mas em que momento você mudaria de +concat para StringBuilder?

não sequestrador
fonte
40
Em que momento você alterna para o StringBuilder? Quando afeta a memória ou o desempenho. Ou quando pode. Se você realmente está fazendo isso apenas por algumas cordas uma vez, não se preocupe. Mas se você estiver repetindo várias vezes, verá uma diferença mensurável ao usar o StringBuilder.
eWall
Qual é a média de 100 no parâmetro?
Asif Mushtaq 14/03
2
@Unknown 100 é o tamanho inicial de StringBuilder
não Sequitor
@nonsequitor Então, o máximo de caracteres será 100?
Asif Mushtaq
10
@Desconhecido não apenas o tamanho inicial, se você souber o tamanho aproximado da string com a qual está lidando, poderá dizer StringBuilderquanto tamanho será alocado antecipadamente, caso contrário, se ficar sem espaço, terá que dobrar o tamanho criando um Uma nova char[]matriz copia os dados - o que é caro. Você pode trapacear, fornecendo o tamanho e, em seguida, não há necessidade dessa criação de matriz - portanto, se você acha que sua string terá aproximadamente 100 caracteres, poderá definir o StringBuilder para esse tamanho e nunca precisará ser expandido internamente.
Non-sequitor

Respostas:

964

A versão 1 é preferível porque é mais curta e o compilador a transformará na versão 2 - nenhuma diferença de desempenho.

Mais importante, porque temos apenas três propriedades, pode não fazer diferença, mas em que momento você alterna de concat para construtor?

No ponto em que você está concatenando em um loop - geralmente é quando o compilador não pode substituir StringBuilderpor si mesmo.

Michael Borgwardt
fonte
19
Isso é verdade, mas a referência de idioma também afirma que isso é opcional. Na verdade, eu fiz um teste simples com o JRE 1.6.0_15 e não vi nenhuma otimização do compilador na classe descompilada.
11789 bruno conde
37
Eu apenas tentei o código da pergunta (compilado no JDK 1.6.0_16) e encontrei a otimização conforme o esperado. Tenho certeza de que todos os compiladores modernos farão isso.
227 Michael Borgwardt
22
Você está certo. Olhando para o bytecode, posso ver claramente a otimização do StringBuilder. Eu estava usando um descompilador e, de alguma forma, ele está convertendo novamente para concat. +1
bruno conde
80
Não para vencer um cavalo morto, mas a redação das especificações é: To increase the performance of repeated string concatenation, a Java compiler _may_ use the StringBuffer class or a similar technique to reduce the number of intermediate String objects that are created by evaluation of an expression.A palavra-chave que pode existir em maio . Dado que isso é oficialmente opcional (embora provavelmente implementado), não devemos nos proteger?
Lucas
93
@ Lucas: Não, não devemos. Se o compilador decidir não executar essa otimização, será porque não vale a pena. Em 99% dos casos, o compilador sabe melhor qual otimização vale a pena; portanto, como regra geral, o desenvolvedor não deve interferir. Obviamente, sua situação pode cair nos outros 1%, mas isso só pode ser verificado por meio de benchmarking (cuidadoso).
sleske
255

A chave é se você está escrevendo uma única concatenação em um único local ou acumulando-a com o tempo.

Para o exemplo que você deu, não faz sentido usar explicitamente o StringBuilder. (Veja o código compilado para o seu primeiro caso.)

Mas se você estiver construindo uma string, por exemplo, dentro de um loop, use StringBuilder.

Para esclarecer, assumindo que o enormeArray contenha milhares de strings, codifique assim:

...
String result = "";
for (String s : hugeArray) {
    result = result + s;
}

desperdiça muito tempo e memória em comparação com:

...
StringBuilder sb = new StringBuilder();
for (String s : hugeArray) {
    sb.append(s);
}
String result = sb.toString();
joel.neely
fonte
3
Sim, o StringBuilder não precisa recriar o objeto String repetidamente.
Olga #
124
Caramba eu usei aqueles 2 função para testar um grande corda Estou trabalhando em 6.51min vs 11secs.
user1722791
1
A propósito, você também pode usar result += s;(no primeiro exemplo)
RAnders00
1
quantos objetos serão criados por esta declaração? "{a:"+ a + ", b:" + b + ", c: " + c +"}";
Asif Mushtaq
1
Que tal algo como: String str = (a == null)? nulo: a '+ (b == nulo)? nulo: b '+ (c == nulo)? c: c '+ ...; ? Isso impedirá que a otimização aconteça?
Amitfr 31/07/19
75

Eu prefiro:

String.format( "{a: %s, b: %s, c: %s}", a, b, c );

... porque é curto e legível.

Eu não otimizaria isso em termos de velocidade, a menos que você o usasse em um loop com uma contagem de repetições muito alta e medisse a diferença de desempenho.

Concordo que, se você precisar gerar muitos parâmetros, esse formulário poderá ficar confuso (como um dos comentários diz). Nesse caso, eu mudaria para uma forma mais legível (talvez usando o ToStringBuilder do apache-commons - retirado da resposta de matt b) e ignoraria o desempenho novamente.

tangens
fonte
64
Na verdade, é mais longo, contém mais símbolos e possui variáveis ​​de texto fora de sequência.
Tom Hawtin - tackline
4
Então, você diria que é menos legível do que uma das outras abordagens?
Tangens
3
Prefiro escrever isso, porque é mais fácil adicionar mais variáveis, mas não tenho certeza de que seja mais legível - principalmente porque o número de argumentos aumenta. Também não funciona em momentos em que você precisa adicionar bits em momentos diferentes.
Alex Feinman
78
Parece mais difícil de ler (para mim). Agora eu tenho que digitalizar entre {...} e os parâmetros.
Steve Kuo
10
Prefiro este formulário a, porque é seguro se um dos parâmetros fornull
rds
74

Na maioria dos casos, você não verá uma diferença real entre as duas abordagens, mas é fácil construir um cenário de pior caso como este:

public class Main
{
    public static void main(String[] args)
    {
        long now = System.currentTimeMillis();
        slow();
        System.out.println("slow elapsed " + (System.currentTimeMillis() - now) + " ms");

        now = System.currentTimeMillis();
        fast();
        System.out.println("fast elapsed " + (System.currentTimeMillis() - now) + " ms");
    }

    private static void fast()
    {
        StringBuilder s = new StringBuilder();
        for(int i=0;i<100000;i++)
            s.append("*");      
    }

    private static void slow()
    {
        String s = "";
        for(int i=0;i<100000;i++)
            s+="*";
    }
}

A saída é:

slow elapsed 11741 ms
fast elapsed 7 ms

O problema é que + = anexar a uma string reconstrói uma nova string, por isso custa algo linear ao comprimento de suas strings (soma de ambas).

Então - para sua pergunta:

A segunda abordagem seria mais rápida, mas é menos legível e mais difícil de manter. Como eu disse, no seu caso específico, você provavelmente não perceberia a diferença.

Omry Yadan
fonte
Não se esqueça de .concat (). Suponho que o tempo decorrido esteja entre 10 e 18 ms, tornando-o desprezível ao usar sequências curtas, como no exemplo original do post.
Droo 07/10/2009
9
Enquanto você está certo +=, o exemplo original foi uma sequência de +que o compilador transforma em uma única string.concatchamada. Seus resultados não se aplicam.
Blindy
1
@Blindy & Droo: - Vocês dois estão certos. Usar .concate nesse cenário é a melhor solução, pois + = cria um novo objeto toda vez que a rotina de loop é executada.
precisa saber é o seguinte
3
você sabia que o toString () dele não é chamado em loop?
Omry Yadan
Eu tentei este exemplo para testar a velocidade. Portanto, meus resultados são: 29672 ms decorridos lentos; rápido decorrido 15 ms. Então a resposta é óbvia. Mas se fossem 100 iterações - o tempo é o mesmo - 0 ms. Se 500 iterações - 16 ms e 0 ms. E assim por diante.
Ernestas Gruodis 27/08
28

Eu também tive um conflito com meu chefe sobre o fato de usar append ou +. Como eles estão usando Append (eu ainda não consigo descobrir como eles dizem toda vez que um novo objeto é criado). Embora eu goste de explicar Michael Borgwardt, eu só queria mostrar uma explicação se alguém realmente precisa saber no futuro.

/**
 *
 * @author Perilbrain
 */
public class Appc {
    public Appc() {
        String x = "no name";
        x += "I have Added a name" + "We May need few more names" + Appc.this;
        x.concat(x);
        // x+=x.toString(); --It creates new StringBuilder object before concatenation so avoid if possible
        //System.out.println(x);
    }

    public void Sb() {
        StringBuilder sbb = new StringBuilder("no name");
        sbb.append("I have Added a name");
        sbb.append("We May need few more names");
        sbb.append(Appc.this);
        sbb.append(sbb.toString());
        // System.out.println(sbb.toString());
    }
}

e desmontagem da classe acima sai como

 .method public <init>()V //public Appc()
  .limit stack 2
  .limit locals 2
met001_begin:                                  ; DATA XREF: met001_slot000i
  .line 12
    aload_0 ; met001_slot000
    invokespecial java/lang/Object.<init>()V
  .line 13
    ldc "no name"
    astore_1 ; met001_slot001
  .line 14

met001_7:                                      ; DATA XREF: met001_slot001i
    new java/lang/StringBuilder //1st object of SB
    dup
    invokespecial java/lang/StringBuilder.<init>()V
    aload_1 ; met001_slot001
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    ldc "I have Added a nameWe May need few more names"
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    aload_0 ; met001_slot000
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lan\
g/StringBuilder;
    invokevirtual java/lang/StringBuilder.toString()Ljava/lang/String;
    astore_1 ; met001_slot001
  .line 15
    aload_1 ; met001_slot001
    aload_1 ; met001_slot001
    invokevirtual java/lang/String.concat(Ljava/lang/String;)Ljava/lang/Strin\
g;
    pop
  .line 18
    return //no more SB created
met001_end:                                    ; DATA XREF: met001_slot000i ...

; ===========================================================================

;met001_slot000                                ; DATA XREF: <init>r ...
    .var 0 is this LAppc; from met001_begin to met001_end
;met001_slot001                                ; DATA XREF: <init>+6w ...
    .var 1 is x Ljava/lang/String; from met001_7 to met001_end
  .end method
;44-1=44
; ---------------------------------------------------------------------------


; Segment type: Pure code
  .method public Sb()V //public void Sb
  .limit stack 3
  .limit locals 2
met002_begin:                                  ; DATA XREF: met002_slot000i
  .line 21
    new java/lang/StringBuilder
    dup
    ldc "no name"
    invokespecial java/lang/StringBuilder.<init>(Ljava/lang/String;)V
    astore_1 ; met002_slot001
  .line 22

met002_10:                                     ; DATA XREF: met002_slot001i
    aload_1 ; met002_slot001
    ldc "I have Added a name"
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    pop
  .line 23
    aload_1 ; met002_slot001
    ldc "We May need few more names"
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    pop
  .line 24
    aload_1 ; met002_slot001
    aload_0 ; met002_slot000
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lan\
g/StringBuilder;
    pop
  .line 25
    aload_1 ; met002_slot001
    aload_1 ; met002_slot001
    invokevirtual java/lang/StringBuilder.toString()Ljava/lang/String;
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    pop
  .line 28
    return
met002_end:                                    ; DATA XREF: met002_slot000i ...


;met002_slot000                                ; DATA XREF: Sb+25r
    .var 0 is this LAppc; from met002_begin to met002_end
;met002_slot001                                ; DATA XREF: Sb+9w ...
    .var 1 is sbb Ljava/lang/StringBuilder; from met002_10 to met002_end
  .end method
;96-49=48
; ---------------------------------------------------------------------------

Dos dois códigos acima, você pode ver que Michael está certo. Em cada caso, apenas um objeto SB é criado.

perilbrain
fonte
27

Desde o Java 1.5, a concatenação simples de uma linha com "+" e StringBuilder.append () gera exatamente o mesmo bytecode.

Portanto, para facilitar a leitura do código, use "+".

2 exceções:

  • ambiente multithread: StringBuffer
  • concatenação em loops: StringBuilder / StringBuffer
Zofren
fonte
22

Usando a versão mais recente do Java (1.8), a desmontagem ( javap -c) mostra a otimização introduzida pelo compilador. +também sb.append()irá gerar um código muito semelhante. No entanto, vale a pena inspecionar o comportamento se estivermos usando+ um loop for.

Adicionando strings usando + em um loop for

Java:

public String myCatPlus(String[] vals) {
    String result = "";
    for (String val : vals) {
        result = result + val;
    }
    return result;
}

ByteCode :( fortrecho de loop)

12: iload         5
14: iload         4
16: if_icmpge     51
19: aload_3
20: iload         5
22: aaload
23: astore        6
25: new           #3                  // class java/lang/StringBuilder
28: dup
29: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
32: aload_2
33: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: aload         6
38: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
41: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
44: astore_2
45: iinc          5, 1
48: goto          12

Adicionando strings usando stringbuilder.append

Java:

public String myCatSb(String[] vals) {
    StringBuilder sb = new StringBuilder();
    for(String val : vals) {
        sb.append(val);
    }
    return sb.toString();
}

ByteCdoe :( fortrecho do loop)

17: iload         5
19: iload         4
21: if_icmpge     43
24: aload_3
25: iload         5
27: aaload
28: astore        6
30: aload_2
31: aload         6
33: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: pop
37: iinc          5, 1
40: goto          17
43: aload_2

Há um pouco de diferença gritante embora. No primeiro caso, onde +foi usado, novo StringBuilderé criado para cada iteração de loop e o resultado gerado é armazenado fazendo uma toString()chamada (29 a 41). Então você está gerando Strings intermediárias que você realmente não precisa enquanto usa o +operador em forloop.

portador de anel
fonte
1
Este é o Oracle JDK ou OpenJDK?
Christophe Roussy
12

No Java 9, a versão 1 deve ser mais rápida porque é convertida em invokedynamicchamada. Mais detalhes podem ser encontrados no JEP-280 :

A idéia é substituir a dança anexada inteira do StringBuilder por uma chamada dinâmica invocada simples para java.lang.invoke.StringConcatFactory, que aceitará os valores na necessidade de concatenação.

ZhekaKozlov
fonte
9

Por motivos de desempenho, o uso de +=( Stringconcatenação) é desencorajado. O motivo é: Java Stringé imutável, toda vez que uma nova concatenação é feita, uma nova Stringé criada (a nova tem uma impressão digital diferente da mais antiga já existente no pool de String ). A criação de novas strings pressiona o GC e diminui a velocidade do programa: a criação de objetos é cara.

O código abaixo deve torná-lo mais prático e claro ao mesmo tempo.

public static void main(String[] args) 
{
    // warming up
    for(int i = 0; i < 100; i++)
        RandomStringUtils.randomAlphanumeric(1024);
    final StringBuilder appender = new StringBuilder();
    for(int i = 0; i < 100; i++)
        appender.append(RandomStringUtils.randomAlphanumeric(i));

    // testing
    for(int i = 1; i <= 10000; i*=10)
        test(i);
}

public static void test(final int howMany) 
{
    List<String> samples = new ArrayList<>(howMany);
    for(int i = 0; i < howMany; i++)
        samples.add(RandomStringUtils.randomAlphabetic(128));

    final StringBuilder builder = new StringBuilder();
    long start = System.nanoTime();
    for(String sample: samples)
        builder.append(sample);
    builder.toString();
    long elapsed = System.nanoTime() - start;
    System.out.printf("builder - %d - elapsed: %dus\n", howMany, elapsed / 1000);

    String accumulator = "";
    start = System.nanoTime();
    for(String sample: samples)
        accumulator += sample;
    elapsed = System.nanoTime() - start;
    System.out.printf("concatenation - %d - elapsed: %dus\n", howMany, elapsed / (int) 1e3);

    start = System.nanoTime();
    String newOne = null;
    for(String sample: samples)
        newOne = new String(sample);
    elapsed = System.nanoTime() - start;
    System.out.printf("creation - %d - elapsed: %dus\n\n", howMany, elapsed / 1000);
}

Os resultados de uma corrida são relatados abaixo.

builder - 1 - elapsed: 132us
concatenation - 1 - elapsed: 4us
creation - 1 - elapsed: 5us

builder - 10 - elapsed: 9us
concatenation - 10 - elapsed: 26us
creation - 10 - elapsed: 5us

builder - 100 - elapsed: 77us
concatenation - 100 - elapsed: 1669us
creation - 100 - elapsed: 43us

builder - 1000 - elapsed: 511us
concatenation - 1000 - elapsed: 111504us
creation - 1000 - elapsed: 282us

builder - 10000 - elapsed: 3364us 
concatenation - 10000 - elapsed: 5709793us
creation - 10000 - elapsed: 972us

Sem considerar os resultados de 1 concatenação (o JIT ainda não estava fazendo seu trabalho), mesmo em 10 concatenações, a penalidade de desempenho é relevante; para milhares de concatenações, a diferença é enorme.

Lições aprendidas com esse experimento muito rápido (facilmente reproduzível com o código acima): nunca use o +=para concatenar seqüências de caracteres juntas, mesmo em casos muito básicos em que algumas concatenações são necessárias (como dito, criar novas cadeias de caracteres é caro de qualquer maneira e pressiona o GC).

Paolo Maresca
fonte
9

Veja o exemplo abaixo:

static final int MAX_ITERATIONS = 50000;
static final int CALC_AVG_EVERY = 10000;

public static void main(String[] args) {
    printBytecodeVersion();
    printJavaVersion();
    case1();//str.concat
    case2();//+=
    case3();//StringBuilder
}

static void case1() {
    System.out.println("[str1.concat(str2)]");
    List<Long> savedTimes = new ArrayList();
    long startTimeAll = System.currentTimeMillis();
    String str = "";
    for (int i = 0; i < MAX_ITERATIONS; i++) {
        long startTime = System.currentTimeMillis();
        str = str.concat(UUID.randomUUID() + "---");
        saveTime(savedTimes, startTime);
    }
    System.out.println("Created string of length:" + str.length() + " in " + (System.currentTimeMillis() - startTimeAll) + " ms");
}

static void case2() {
    System.out.println("[str1+=str2]");
    List<Long> savedTimes = new ArrayList();
    long startTimeAll = System.currentTimeMillis();
    String str = "";
    for (int i = 0; i < MAX_ITERATIONS; i++) {
        long startTime = System.currentTimeMillis();
        str += UUID.randomUUID() + "---";
        saveTime(savedTimes, startTime);
    }
    System.out.println("Created string of length:" + str.length() + " in " + (System.currentTimeMillis() - startTimeAll) + " ms");
}

static void case3() {
    System.out.println("[str1.append(str2)]");
    List<Long> savedTimes = new ArrayList();
    long startTimeAll = System.currentTimeMillis();
    StringBuilder str = new StringBuilder("");
    for (int i = 0; i < MAX_ITERATIONS; i++) {
        long startTime = System.currentTimeMillis();
        str.append(UUID.randomUUID() + "---");
        saveTime(savedTimes, startTime);
    }
    System.out.println("Created string of length:" + str.length() + " in " + (System.currentTimeMillis() - startTimeAll) + " ms");

}

static void saveTime(List<Long> executionTimes, long startTime) {
    executionTimes.add(System.currentTimeMillis() - startTime);
    if (executionTimes.size() % CALC_AVG_EVERY == 0) {
        out.println("average time for " + executionTimes.size() + " concatenations: "
                + NumberFormat.getInstance().format(executionTimes.stream().mapToLong(Long::longValue).average().orElseGet(() -> 0))
                + " ms avg");
        executionTimes.clear();
    }
}

Resultado:

java bytecode versão: 8
java.version: 1.8.0_144
[str1.concat (str2)]
tempo médio para 10000 concatenações: 0,096 ms
tempo médio para 10000 concatenações: 0,185 ms
tempo médio para 10000 concatenações: 0,327 ms
tempo médio para 10000 concatenações: 0,501 ms
tempo médio para 10000 concatenações: 0,656 ms méd
. Sequência de caracteres criada de comprimento: 1950000 em 17745 ms
[str1 + = str2]
tempo médio para 10000 concatenações: 0,21 ms
tempo médio para 10000 concatenações: 0,652 ms
tempo médio para 10000 concatenações: 1.129 ms
tempo médio para 10000 concatenações: 1.727 ms
tempo médio para 10000 concatenações: 2,302 ms avg
Cadeia de comprimento criada: 1950000 em 60279 ms
[str1.append (str2)]
tempo médio para 10000 concatenações: 0,002 ms
tempo médio para 10000 concatenações: 0,002 ms
tempo médio para 10000 concatenações: 0,002 ms
tempo médio para 10000 concatenações: Tempo médio
médio de 0,002 ms para 10000 concatenações: 0,002 ms média de
sequência criada: 1950000 in 100 ms

À medida que o comprimento da cadeia aumenta, o tempo de concatenação também aumenta.
É aí que StringBuilderdefinitivamente é necessário.
Como você vê, a concatenação:, UUID.randomUUID()+"---"realmente não afeta o tempo.

PS: Eu não acho que quando usar o StringBuilder em Java seja realmente uma duplicata disso.
Esta pergunta fala sobre o toString()que na maioria das vezes não executa concatenações de grandes seqüências de caracteres.


Atualização de 2019

Desde os java8tempos, as coisas mudaram um pouco. Parece que agora (java13), o tempo de concatenação de +=é praticamente o mesmo que str.concat(). No entanto, o StringBuildertempo de concatenação ainda é constante . (A postagem original acima foi ligeiramente editada para adicionar uma saída mais detalhada)

java bytecode versão: 13
java.version: 13.0.1
[str1.concat (str2)]
tempo médio para 10000 concatenações: 0,047 ms
tempo médio para 10000 concatenações: 0,1 ms
tempo médio para 10000 concatenações: 0,17 ms
tempo médio para 10000 concatenações: 0,255 ms
tempo médio para 10000 concatenações: 0,336 ms méd
. Sequência de caracteres criada de comprimento: 1950000 em 9147 ms
[str1 + = str2]
tempo médio para 10000 concatenações: 0,037 ms
tempo médio para 10000 concatenações: 0,097 ms
tempo médio para 10000 concatenações: 0.249 ms
tempo médio para 10000 concatenações: 0.298 ms
tempo médio para 10000 concatenações: 0,326 ms avg
Cadeia de comprimento criada: 1950000 em 10191 ms
[str1.append (str2)]
tempo médio para 10000 concatenações: 0,001 ms
tempo médio para 10000 concatenações: 0,001 ms
tempo médio para 10000 concatenações: 0,001 ms
tempo médio para 10000 concatenações: Tempo médio
médio de 0,001 ms para 10000 concatenações: 0,001 ms média de
sequência criada: 1950000 in 43 ms

É importante notar que a bytecode:8/java.version:13combinação tem um bom benefício de desempenho em comparação combytecode:8/java.version:8

Marinos An
fonte
Esta deve ser a resposta aceita .. isso depende do tamanho da corda sequência que determina a escolha do concat ou StringBuilder
user1428716
@ user1428716 FYI: resposta atualizada com resultados dos java13quais agora são diferentes. Mas acho que a principal conclusão permanece a mesma.
Marinos #
7

O Apache Commons-Lang possui uma classe ToStringBuilder que é super fácil de usar. Ele faz um bom trabalho manipulando a lógica de acréscimo, bem como a formatação de como você deseja que o seu toString pareça.

public void toString() {
     ToStringBuilder tsb =  new ToStringBuilder(this);
     tsb.append("a", a);
     tsb.append("b", b)
     return tsb.toString();
}

Retornará a saída que parece com.blah.YourClass@abc1321f[a=whatever, b=foo].

Ou de uma forma mais condensada usando encadeamento:

public void toString() {
     return new ToStringBuilder(this).append("a", a).append("b", b").toString();
}

Ou se você quiser usar a reflexão para incluir todos os campos da classe:

public String toString() {
    return ToStringBuilder.reflectionToString(this);
}

Você também pode personalizar o estilo do ToString, se desejar.

matt b
fonte
4

Torne o método toString o mais legível possível!

A única exceção para isso no meu livro é se você puder provar que consome recursos significativos :) (Sim, isso significa criação de perfil)

Observe também que o compilador Java 5 gera código mais rápido que a abordagem "StringBuffer" manuscrita usada nas versões anteriores do Java. Se você usar "+", este e os aprimoramentos futuros serão gratuitos.

Thorbjørn Ravn Andersen
fonte
3

Parece haver algum debate sobre se o uso do StringBuilder ainda é necessário nos compiladores atuais. Então pensei em dar meus 2 centavos de experiência.

Tenho um JDBCconjunto de resultados de 10 mil registros (sim, preciso de todos eles em um lote.) O uso do operador + leva cerca de 5 minutos na minha máquina Java 1.8. UsandostringBuilder.append("") leva menos de um segundo para a mesma consulta.

Então a diferença é enorme. Dentro de um loop StringBuilderé muito mais rápido.

Eddy
fonte
2
Eu acho que o debate é sobre usá-lo fora dos loops. Eu acho que existe um consenso de que você precisa usá-lo dentro de um loop.
Jordan
2

A concatenação de String em termos de desempenho usando '+' é mais cara porque precisa fazer uma cópia totalmente nova de String, já que Strings são imutáveis ​​em java. Isso desempenha um papel particular se a concatenação for muito frequente, por exemplo: dentro de um loop. A seguir, o que minha IDEA sugere quando tento fazer uma coisa dessas:

insira a descrição da imagem aqui

Regras gerais:

  • Dentro de uma única atribuição de sequência, o uso da concatenação de sequência é bom.
  • Se você estiver fazendo um loop para criar um grande bloco de dados de caracteres, vá para StringBuffer.
  • Usar + = em uma String sempre será menos eficiente do que usar um StringBuffer, portanto, deve soar sinais de aviso - mas, em certos casos, a otimização obtida será insignificante em comparação com os problemas de legibilidade, portanto, use seu bom senso.

Aqui está um bom blog de Jon Skeet sobre esse tópico.

Sudip Bhandari
fonte
2
Você nunca deve usar StringBuffer, a menos que precise absolutamente de acesso sincronizado de vários threads. Caso contrário, prefira StringBuilder que não está sincronizado e, portanto, possui menos sobrecarga.
Erki der Loony
1

Posso salientar que, se você vai repetir uma coleção e usar o StringBuilder, pode consultar o Apache Commons Lang e o StringUtils.join () (em diferentes tipos)?

Independentemente do desempenho, você economizará a necessidade de criar StringBuilders e de loops pela milionésima vez.

Brian Agnew
fonte
1

Aqui está o que eu verifiquei em Java8

  • Usando concatenação de String
  • Usando StringBuilder

    long time1 = System.currentTimeMillis();
    usingStringConcatenation(100000);
    System.out.println("usingStringConcatenation " + (System.currentTimeMillis() - time1) + " ms");
    
    time1 = System.currentTimeMillis();
    usingStringBuilder(100000);
    System.out.println("usingStringBuilder " + (System.currentTimeMillis() - time1) + " ms");
    
    
    private static void usingStringBuilder(int n)
    {
        StringBuilder str = new StringBuilder();
        for(int i=0;i<n;i++)
            str.append("myBigString");    
    }
    
    private static void usingStringConcatenation(int n)
    {
        String str = "";
        for(int i=0;i<n;i++)
            str+="myBigString";
    }

É realmente um pesadelo se você estiver usando concatenação de strings para um grande número de strings.

usingStringConcatenation 29321 ms
usingStringBuilder 2 ms
nagendra547
fonte
-1

Eu acho que devemos seguir a abordagem de acréscimo de StringBuilder. Razão de ser:

  1. A concatenação de String criará um novo objeto de string a cada vez (como String é um objeto imutável), portanto, criará 3 objetos.

  2. Com o construtor String, apenas um objeto será criado [StringBuilder é mutável] e a string adicional será anexada a ele.

VishalTambe
fonte
Por que esta resposta foi reduzida? docs.oracle.com/javase/8/docs/api/java/util/stream/… - Redução
mutável
-4

Para cordas simples como essa eu prefiro usar

"string".concat("string").concat("string");

Em ordem, eu diria que o método preferido de construir uma string é usando StringBuilder, String # concat () e, em seguida, o operador + sobrecarregado. O StringBuilder é um aumento significativo no desempenho ao trabalhar com cadeias grandes, assim como o uso do operador + é uma grande diminuição no desempenho (diminuição exponencialmente grande à medida que o tamanho da cadeia aumenta). O único problema ao usar .concat () é que ele pode lançar NullPointerExceptions.

Droo
fonte
9
É provável que o uso de concat () tenha desempenho pior que '+', pois o JLS permite que '+' seja convertido em um StringBuilder, e provavelmente todas as JVMs o fazem ou usam uma alternativa mais eficiente - o mesmo provavelmente não se aplica a concat que deve criar e descartar pelo menos uma sequência intermediária completa no seu exemplo.
Lawrence Dol