String vs. StringBuilder

Eu entendo a diferença entre String e StringBuilder ( StringBuilder sendo mutável), mas há uma grande diferença de desempenho entre os dois?

O programa em que estou trabalhando tem muitos anexos de strings acionados por maiúsculas e minúsculas (500+). Está usando StringBuilder uma escolha melhor?

   

Sim, a diferença de desempenho é significativa. Consulte o artigo da base de conhecimento ” Como melhorar o desempenho da concatenação de strings no Visual C # “.

Sempre tentei codificar por clareza primeiro e, depois, otimizar o desempenho posteriormente. Isso é muito mais fácil do que fazer o contrário! No entanto, tendo visto a enorme diferença de desempenho em meus aplicativos entre os dois, agora penso nisso com um pouco mais de cuidado.

Felizmente, é relativamente simples executar a análise de desempenho em seu código para ver onde você está gastando o tempo e modificá-lo para usar o StringBuilder onde necessário.

Para esclarecer o que Gillian disse sobre 4 cordas, se você tem algo assim:

 string a,b,c,d; a = b + c + d; 

então seria mais rápido usando strings e o operador plus. Isso porque (como o Java, como Eric aponta), ele usa o StringBuilder automaticamente (na verdade, ele usa uma primitiva que o StringBuilder também usa)

No entanto, se o que você está fazendo está mais próximo de:

 string a,b,c,d; a = a + b; a = a + c; a = a + d; 

Então você precisa usar explicitamente um StringBuilder. .Net não cria automaticamente um StringBuilder aqui, porque seria inútil. No final de cada linha, “a” tem que ser uma string (imutável), então ele teria que criar e dispor um StringBuilder em cada linha. Para velocidade, você precisaria usar o mesmo StringBuilder até terminar de construir:

 string a,b,c,d; StringBuilder e = new StringBuilder(); e.Append(b); e.Append(c); e.Append(d); a = e.ToString(); 

O StringBuilder é preferível se você estiver fazendo vários loops, ou garfos no seu código pass … no entanto, para o desempenho PURE, se você puder obter uma declaração de string SINGLE , ela terá muito mais desempenho.

Por exemplo:

 string myString = "Some stuff" + var1 + " more stuff" + var2 + " other stuff" .... etc... etc...; 

tem mais desempenho que

 StringBuilder sb = new StringBuilder(); sb.Append("Some Stuff"); sb.Append(var1); sb.Append(" more stuff"); sb.Append(var2); sb.Append("other stuff"); // etc.. etc.. etc.. 

Nesse caso, StringBuild pode ser considerado mais fácil de manter, mas não tem mais desempenho que a declaração de cadeia única.

9 vezes fora de 10 … use o construtor de string.

Em uma nota lateral: string + var também tem mais desempenho que a string.Format (geralmente) que usa um StringBuilder internamente (quando em dúvida … verifique o refletor!)

Este benchmark mostra que a concatenação regular é mais rápida quando se combina 3 ou menos strings.

http://www.chinhdo.com/20070224/stringbuilder-is-not-always-faster/

O StringBuilder pode fazer uma melhoria muito significativa no uso da memory, especialmente no seu caso de adicionar 500 strings juntas.

Considere o seguinte exemplo:

 string buffer = "The numbers are: "; for( int i = 0; i < 5; i++) { buffer += i.ToString(); } return buffer; 

O que acontece na memory? As seguintes strings são criadas:

 1 - "The numbers are: " 2 - "0" 3 - "The numbers are: 0" 4 - "1" 5 - "The numbers are: 01" 6 - "2" 7 - "The numbers are: 012" 8 - "3" 9 - "The numbers are: 0123" 10 - "4" 11 - "The numbers are: 01234" 12 - "5" 13 - "The numbers are: 012345" 

Ao adicionar esses cinco números ao final da string, criamos 13 objects string! E 12 deles eram inúteis! Uau!

O StringBuilder corrige esse problema. Não é uma "string mutável" como costumamos ouvir ( todas as strings no .NET são imutáveis ). Ele funciona mantendo um buffer interno, uma matriz de char. Chamando Append () ou AppendLine () adiciona a seqüência de caracteres ao espaço vazio no final da matriz de caracteres; se a matriz for muito pequena, ela criará uma nova matriz maior e copia o buffer lá. Portanto, no exemplo acima, StringBuilder pode precisar apenas de uma única matriz para conter todas as 5 adições à string - dependendo do tamanho de seu buffer. Você pode dizer ao StringBuilder qual é o tamanho do seu buffer no construtor.

Um exemplo simples para demonstrar a diferença de velocidade ao usar o método String contactenation vs StringBuilder :

 System.Diagnostics.Stopwatch time = new Stopwatch(); string test = string.Empty; time.Start(); for (int i = 0; i < 100000; i++) { test += i; } time.Stop(); System.Console.WriteLine("Using String contactenation: " + time.ElapsedMilliseconds + " milliseconds"); 

Resultado:

Usando a colocação de cordas: 15423 milissegundos

 StringBuilder test1 = new StringBuilder(); time.Reset(); time.Start(); for (int i = 0; i < 100000; i++) { test1.Append(i); } time.Stop(); System.Console.WriteLine("Using StringBuilder: " + time.ElapsedMilliseconds + " milliseconds"); 

Resultado:

Usando StringBuilder: 10 milissegundos

Como resultado, a primeira iteração levou 15423 ms enquanto a segunda iteração usando StringBuilder levou 10 ms.

Parece-me que usar o StringBuilder é mais rápido, muito mais rápido.

Não vá cegamente para StringBuilder. Existem cenários onde o StringBuilder não melhora o desempenho.

Rico Mariani escreveu duas inputs de blog perspicazes sobre isso:

http://blogs.msdn.com/ricom/archive/2003/12/02/40778.aspx

http://blogs.msdn.com/ricom/archive/2003/12/15/43628.aspx

Sim, o StringBuilder oferece melhor desempenho ao executar operações repetidas em uma string. É porque todas as alterações são feitas em uma única instância, para economizar muito tempo, em vez de criar uma nova instância como String .

String Vs Stringbuilder

  • String

    1. em namespace do System
    2. instância imutável (somente leitura)
    3. o desempenho degrada quando ocorre uma mudança contínua de valor
    4. discussão segura
  • StringBuilder (string mutável)

    1. no namespace System.Text
    2. instância mutável
    3. mostra melhor desempenho desde que novas alterações são feitas na instância existente

Recomendo vivamente o artigo da dotnet mob: String Vs StringBuilder em C # .

Pergunta sobre estouro de pilha relacionada: mutabilidade de string quando a string não muda em c #? .

O StringBuilder reduz o número de alocações e atribuições, a um custo de memory extra usada. Usado corretamente, ele pode remover completamente a necessidade de o compilador alocar strings cada vez maiores até que o resultado seja encontrado.

 string result = ""; for(int i = 0; i != N; ++i) { result = result + i.ToString(); // allocates a new string, then assigns it to result, which gets repeated N times } 

vs.

 String result; StringBuilder sb = new StringBuilder(10000); // create a buffer of 10k for(int i = 0; i != N; ++i) { sb.Append(i.ToString()); // fill the buffer, resizing if it overflows the buffer } result = sb.ToString(); // assigns once 

O desempenho de uma operação de concatenação para um object String ou StringBuilder depende de quantas vezes ocorre uma alocação de memory. Uma operação de concatenação de String sempre aloca memory, enquanto uma operação de concatenação StringBuilder apenas aloca memory se o buffer de object StringBuilder for muito pequeno para acomodar os novos dados. Conseqüentemente, a class String é preferível para uma operação de concatenação se um número fixo de objects String for concatenado. Nesse caso, as operações de concatenação individuais podem até ser combinadas em uma única operação pelo compilador. Um object StringBuilder é preferível para uma operação de concatenação se um número arbitrário de strings for concatenado; por exemplo, se um loop concatenar um número random de strings de input do usuário.

Fonte: MSDN

StringBuilder é melhor para construir uma string a partir de muitos valores não constantes.

Se você estiver construindo uma cadeia de caracteres a partir de muitos valores constantes, como várias linhas de valores em um documento HTML ou XML ou outros trechos de texto, você pode usar apenas a mesma cadeia, porque quase todos os compiladores fazem isso. “folding constante”, um processo de reduzir a tree de análise quando você tem um monte de manipulação constante (também é usado quando você escreve algo como int minutesPerYear = 24 * 365 * 60 ). E para casos simples com valores não constantes acrescentados uns aos outros, o compilador .NET reduzirá seu código para algo semelhante ao que o StringBuilder faz.

Mas quando o seu acréscimo não pode ser reduzido a algo mais simples pelo compilador, você vai querer um StringBuilder . Como salienta, é mais provável que isso aconteça dentro de um loop.

Eu acredito que o StringBuilder é mais rápido se você tiver mais de 4 strings que você precisa append juntas. Além disso, ele pode fazer algumas coisas legais, como o AppendLine.

No .net, StringBuilder é ainda mais rápido do que acrescentar seqüências de caracteres. Tenho certeza de que, em Java, eles criam um StringBuffer sob o capô quando você acrescenta strings, então não há realmente uma diferença. Não sei por que eles ainda não fizeram isso no .NET.

Considere ” A Tragédia Triste do Teatro de Micro-Otimização “.

Usar strings para concatenação pode levar a uma complexidade de tempo de execução na ordem de O(n^2) .

Se você usa um StringBuilder , há muito menos cópias de memory que precisam ser feitas. Com o StringBuilder(int capacity) você pode aumentar o desempenho se puder estimar o tamanho da String final. Mesmo que você não seja preciso, você provavelmente terá que aumentar a capacidade do StringBuilder algumas vezes, o que também pode ajudar no desempenho.

Eu tenho visto ganhos significativos de desempenho usando a chamada do método EnsureCapacity(int capacity) em uma instância de StringBuilder antes de usá-lo para qualquer armazenamento de string. Eu costumo chamar isso na linha de código após a instanciação. Tem o mesmo efeito que se você instanciasse o StringBuilder assim:

 var sb = new StringBuilder(int capacity); 

Essa chamada aloca a memory necessária antecipadamente, o que causa menos alocações de memory durante várias operações Append() . Você tem que adivinhar qual a quantidade de memory necessária, mas para a maioria das aplicações isso não deve ser muito difícil. Eu costumo errar ao lado de um pouco de muita memory (estamos falando 1k ou mais).

Além das respostas anteriores, a primeira coisa que sempre faço quando penso em problemas como este é criar um pequeno aplicativo de teste. Dentro deste aplicativo, execute algum teste de tempo para ambos os cenários e veja por si mesmo o que é mais rápido.

IMHO, acrescentando mais de 500 inputs de string, definitivamente deveria usar o StringBuilder.

String e StringBuilder são na verdade ambos imutáveis, o StringBuilder foi construído em buffers que permitem que seu tamanho seja gerenciado de forma mais eficiente. Quando o StringBuilder precisa resize é quando é realocado no heap. Por padrão, ele é dimensionado para 16 caracteres, você pode definir isso no construtor.

por exemplo.

StringBuilder sb = new StringBuilder (50);

Concatenação de cordas custará mais. Em Java, você pode usar StringBuffer ou StringBuilder com base em sua necessidade. Se você quiser uma implementação sincronizada e segura, vá para StringBuffer. Isso será mais rápido que a concatenação de string.

Se você não precisa de implementação segura ou sincronizada, vá para StringBuilder. Isso será mais rápido que a concatenação de strings e também mais rápido que o StringBuffer, já que não há sobrecarga de synchronization.

O StringBuilder é provavelmente preferível. A razão é que ele aloca mais espaço do que o necessário atualmente (você define o número de caracteres) para deixar espaço para futuros anexos. Então, esses futuros anexos que se encheckboxm no buffer atual não requerem nenhuma alocação de memory ou garbage collection, o que pode ser caro. Em geral, eu uso o StringBuilder para concatenação de string complexa ou formatação múltipla, depois converto para uma String normal quando os dados são completos, e quero um object imutável novamente.

Se você estiver fazendo muita concatenação de strings, use um StringBuilder. Quando você concatena com uma String, você cria uma nova String a cada vez, usando mais memory.

Alex

Como regra geral, se eu tiver que definir o valor da string mais de uma vez, ou se houver algum acréscimo na string, ele precisará ser um construtor de string. Já vi aplicativos que escrevi no passado antes de aprender sobre construtores de cordas que tiveram uma enorme pegada de memory que parece continuar crescendo e crescendo. Alterar esses programas para usar o construtor de string reduz significativamente o uso da memory. Agora eu juro pelo construtor de string.

Minha abordagem sempre foi usar o StringBuilder ao concatenar 4 ou mais strings OU quando eu não sei como podem ocorrer concatenações.

Bom artigo relacionado com o desempenho aqui

StringBuilder é significativamente mais eficiente, mas você não verá esse desempenho, a menos que esteja fazendo uma grande quantidade de modificação de string.

Abaixo está um pedaço rápido de código para dar um exemplo do desempenho. Como você pode ver, você realmente só começa a ver um grande aumento de desempenho quando entra em grandes iterações.

Como você pode ver, as 200.000 iterações levaram 22 segundos enquanto o 1 milhão de iterações usando o StringBuilder foi quase instantâneo.

 string s = string.Empty; StringBuilder sb = new StringBuilder(); Console.WriteLine("Beginning String + at " + DateTime.Now.ToString()); for (int i = 0; i < = 50000; i++) { s = s + 'A'; } Console.WriteLine("Finished String + at " + DateTime.Now.ToString()); Console.WriteLine(); Console.WriteLine("Beginning String + at " + DateTime.Now.ToString()); for (int i = 0; i <= 200000; i++) { s = s + 'A'; } Console.WriteLine("Finished String + at " + DateTime.Now.ToString()); Console.WriteLine(); Console.WriteLine("Beginning Sb append at " + DateTime.Now.ToString()); for (int i = 0; i <= 1000000; i++) { sb.Append("A"); } Console.WriteLine("Finished Sb append at " + DateTime.Now.ToString()); Console.ReadLine(); 

Resultado do código acima:

Começando String + em 28/01/2013 16:55:40.

Terminou String + em 28/01/2013 16:55:40.

Começando String + em 28/01/2013 16:55:40.

Terminou String + em 28/01/2013 16:56:02.

Começando Sb append em 28/01/2013 16:56:02.

Terminado Sb append em 28/01/2013 16:56:02.

O StringBuilder funcionará melhor, do ponto de vista da memory. Quanto ao processamento, a diferença no tempo de execução pode ser insignificante.