Concatenação de Strings

No livro “Effective Java” do Joshua Block, na página 155, ele fala sobre a performance na concatenação de Strings, veja esse exemplo :

// Inappropriate use of string concatenation - Performs horribly !

public String statement() {
   String s = "";
   for (int i = 0; i < numItems(); i++)
      s += lineForItem(i); // String concatenation
   return s;

Daí ele fala para utilizar a classe StringBuffer e até comentou que na máquina dele utilizando o StringBuffer foi 90 vezes mais rápido.


public String statement() {
   StringBuffer s = new StringBuffer(numItems() * LINE_WIDTH);
   for (int i = 0; i < numItems(); i++)
      s.append(lineForItem(i)); // String concatenation
   return s.toString();

Nesse caso ele utilizou o JDK 1.3.

Eu estive vendo a classe StringBuffer na documentação do JDK 1.4.0 e lá diz :

String buffers are used by the compiler to implement the binary string concatenation operator +. For example, the code:

x = “a” + 4 + “c” is compiled to the equivalent of:
x = new StringBuffer().append(“a”).append(4).append(“c”).toString()

which creates a new string buffer (initially empty), appends the string representation of each operand to the string buffer in turn, and then converts the contents of the string buffer to a string. Overall, this avoids creating many temporary strings.

Estive vendo a documentação do JDK 1.3 e é a mesma coisa, será que a performance foi melhor porque ele alocou o StringBuffer com numItems() * LINE_WIDTH ?

O estranho é que ele nem comentou que a contatenação de Strings é implementada como um StringBuffer pelo compilador.

Ainda vou fazer o teste medindo o tempo com um StringBuffer com a alocação default e uma alocação definida pelo Joshua Block.

O que vocês acham ?

Opa,

Num é bem assim q funciona cara. O problema de concatenar Strings, é que elas são imutáveis. Toda vez que se concatena uma String com outra, ela gera uma nova String. E por aí vai. Se vc concatenar muitas Strings, vai gerando muitos objetos na memória o que compromete a performance. A classe StringBuffer não é assim. Ela não gera novos objetos quando se chama o método append(). Ela realmente concatena no mesmo objeto. Por isso a performance dela é bem melhor e é altamente recomendável utilizar StringBuffer ao se fazer uso de concatenações.

exatamente, strings sao imutaveis e StringBuffer sao mutaveis :slight_smile:

quer dizer cada “+” em string cria um objeto novo na memoria…
com StringBuffer isso nao acontece

concordo, que deveria estar escrito mais claramente isso na API…

O que o douglasfs falou não tem a ver com mutévei e imutáveis. Mas pensa assim, douglas: vc vai trocar todos os + por utilização de StringBuffer, certo? O que vc faz com +=??

// original:
s += s + lineForItem(i);
// vira:
s = new StringBuffer().append(s).append(lineForItem(i).toString();

Percebe que do mesmo jeito ele continua construindo várias Strings?
Triste, neh?? O problema é que fazer new é a operação proporcionalmente mais cara em Java.

Eu tive a oportunidade de ver um aumento de performance num código que montava uma query gigantesca com cada um de 18 parâmetros opcionais, sendo que se o parâmetro estava na ordem, ele aparecia no ORDER BY também. Troquei tudo pra StringBuffer e deu um ganho de mais ou menos 30% em qq query, 50% com todos os itens. Isso que o pesado era montar a saída, que tb ficou com StringBuffers.

Hj em dia eu uso StringBuffer até em msgs de debug. Virei maníaco… 8)

[]s

Espero que vc esteja brincando, Duke, pq não tem nada mais feio do que ver um monte de StringBuffer esparramado pelo código onde não acontece nenhum problema de performance. Leia a assinatura do Paulo Silveira de novo pra saber do que eu tou falando :slight_smile:

Aliás, parece que tem um assunto batendo forte no consciente coletivo do GUJ, que é a micro-otimização. Diacho, se vc tá trabalhando com EJBs, WebServices, CORBA e Struts, não vai ser um mero StringBuffer que vai resolver seu problema de performance. Um profiler bem usado vai, quem sabe, te dizer onde está o problema, não um javadoc da java.lang.StringBuffer :wink:

Uma explicação bem interessante do funcionamento da classe StringBuffer versus como os compiladores tratam a concatenação de Strings através do += foi dada na newsletter Java Specialists do mês passado. Há inclusive comparações de performance.
Quem quiser ler, pode acessar em http://www.javaspecialists.co.za/archive/Issue068.html
(Obs.: no momento em que estava escrevendo isso o link estava fora do ar, mas acho que qualquer hora volta…)

Claro, claro, to brincando!!!

Aconteceu uma vez de verdade e de lá pra cá eu fico brincando com isso…

Eu tb acho que a micro otimização é terrível… mas como em Java um programa típico é uma coleção de minúsculos métodos que não fazem quase nada, a tentação de deixar aquele pouco código é grande… mas eu lembro de um trabalho de IA que eu fiz na faculdade há algum tempo…

Era uma busca em uma arvore, e eu tinha um conjunto de nós já olhados. Como os nós em si não podiam ser pintados, eu tinha uma Collection com as opções, e o método equals era usado no processo pra dizer se elas eram equivalentes.

o equals era mesmo muito eficiente, pq eu tinha que utilizálo no código abaixo:

  Collection olhados; // a tal collection
  ...
  protected void processaNoh(Noh novo) {
    if (olhados.contains(novo)) return;
    ... // processa de verdade o nó, gerando mais um monte de nós e colocando este nó entre os olhados.
  }

Eu sabia que o contains usava o equals, por isso fiquei olhando um tempão pra ele, pra fazê-lo o mais eficiente possível. Mas olha a cagada que eu tava fazendo em outro lugar:

  olhados = new Vector();

Mesmo que fosse ArrayList, era com certeza a collection menos indicada pra isso. Acho que ainda não tinha saído o JDK 1.3, mas se não me engano já tinha Hashtable. Como era um projeto de IA, a gente nem se espantou com a lerdeza da coisa, só quando a gente viu os números de outros alunos (aliás, quem comentou sobre usar Hashmap foi o Paulo) que a gente viu o quanto foi inútil se preocupar com o equals.

aquelão!!!