String vs. StringBuilder

215

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

O programa no qual estou trabalhando tem muitos anexos de sequência conduzidos por maiúsculas (500+). Está usando StringBuilderuma escolha melhor?

Kuvo
fonte

Respostas:

236

Sim, a diferença de desempenho é significativa. Consulte o artigo da KB " Como melhorar o desempenho da concatenação de cadeias de caracteres no Visual C # ".

Eu sempre tentei codificar para obter 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 uma análise de desempenho no seu código para ver onde você está gastando o tempo e modificá-lo para usá-lo StringBuilderquando necessário.

Jay Bazuzi
fonte
44
Uma boa regra geral é usar Strings quando você não vai alterá-lo e usar StringBuilder se você o estiver alterando.
Tim
31
Eu gosto muito desta resposta, especialmente o conselho de codificar para maior clareza antes do desempenho. Como desenvolvedores, gastamos tanto ou mais tempo lendo o código quanto o escrevemos.
Scott Lawrence
Fora da lei: acho que isso deve se tornar sua própria resposta que é votada separadamente, se eu entender o StackOverflow corretamente.
Jay Bazuzi 17/09/08
2
com base na minha experiência, se você está tentando concatenar em torno de 10 a 15 cordas, pode usar cordas, mas se não. de cordas são mais do que isso, em seguida, construtor seqüência de uso
Peeyush
3
Ele ainda depende de como se usa, para uma referência a realmente testa I como Coding Horror - A triste tragédia de Micro-Otimização Teatro
Erik Philips
56

Para esclarecer o que Gillian disse sobre 4 cordas, se você tiver algo parecido com isto:

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

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

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

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

Então você precisa usar explicitamente um StringBuilder. O .Net não cria automaticamente um StringBuilder aqui, porque seria inútil. No final de cada linha, "a" deve ser uma sequência (imutável), para que seja necessário criar e dispor um StringBuilder em cada linha. Para velocidade, você precisará usar o mesmo StringBuilder até concluir a construção:

string a,b,c,d;
StringBuilder e = new StringBuilder();
 e.Append(b);
 e.Append(c);
 e.Append(d);
 a = e.ToString();
James Curran
fonte
5
Não há razão para que o compilador C # precise tratar a segunda amostra de maneira diferente da primeira. Em particular, não há obrigação de produzir uma string no final de cada linha. O compilador pode se comportar como você diz, mas não tem obrigação de fazê-lo.
CodesInChaos
@CodesInChaos, uma variável de sequência imutável está sendo atribuída especificamente no final de cada linha, isso não cria a obrigação de produzir uma sequência? No entanto, eu concordo com o ponto de que não há razão para tratar cada linha individual de maneira diferente (e não tenho certeza se isso ocorre); a perda de desempenho vem da realocação, portanto, não importa.
Saeb Amini
@SaebAmini - Se afor uma variável local e o objeto que ele referenciar não tiver sido atribuído a alguma outra variável (que pode ser acessível a outro encadeamento), um bom otimizador poderá determinar que anão é acessado por nenhum outro código durante essa sequência de linhas; apenas o valor final das aquestões. Portanto, poderia tratar essas três linhas de código como se elas fossem escritas a = b + c + d;.
Home
29

É preferível o StringBuilder se você estiver executando vários loops ou bifurcações na sua passagem de código ... no entanto, para obter um desempenho PURE, se você puder se safar com uma declaração de string ÚNICA , será muito mais eficiente.

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 sustentável, mas não tem mais desempenho do que a declaração de string única.

9 vezes em 10, no entanto ... use o construtor de strings.

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

calebjenkins
fonte
17
Eu gostaria que você tivesse dito como você sabe disso / como verificar isso.
21320 ChrisW
2
Você não verifica o desempenho no refletor: verifica o desempenho cronometrando o código de liberação, analisa com um criador de perfil e procura explicações com o refletor.
Albin Sunnanbo 26/10/10
3
Considere usar String.Format () para concatenar um pequeno número de pequenas cadeias, principalmente se o objetivo é formatar mensagens para mostrar ao usuário.
Gary Kindel
1
Esta é uma informação muito ruim. ("string myString é mais eficiente") não é verdadeira.
quer
3
As informações nesta resposta estão incorretas. StringBuilder é um pouco mais rápido que a concatenação feita na mesma instrução; no entanto, você só notará uma diferença entre os dois se fizer isso centenas de milhares de vezes ( origem ). Como dito na fonte, "simplesmente não importa!"
David Sherret
25

Um exemplo simples para demonstrar a diferença de velocidade ao usar Stringconcatenação 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 concatenation: " + time.ElapsedMilliseconds + " milliseconds");

Resultado:

Usando concatenação de String: 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 StringBuilderlevou 10 ms.

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

Diizzy
fonte
24

Essa referência mostra que a concatenação regular é mais rápida ao combinar 3 ou menos seqüências de caracteres.

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

O StringBuilder pode fazer uma melhoria muito significativa no uso da memória, especialmente no caso de adicionar 500 strings.

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 memória? As seguintes seqüências de caracteres 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"

Adicionando esses cinco números ao final da string, criamos 13 objetos de string! E 12 deles eram inúteis! Uau!

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

Matt Trunnell
fonte
2
Menor nit: é um pouco estranho dizer "criamos 13 objetos de string e 12 deles eram inúteis" e, em seguida, dizer que o StringBuilder corrige esse problema. Afinal, seis das cordas você não tem escolha a não ser criar; eles são de i.ToString(). Portanto, com o StringBuilder, você ainda precisa criar 6 + 1 strings; reduziu a criação de 13 strings para a criação de 7 strings. Mas essa ainda é a maneira errada de encarar; a criação das seis seqüências numéricas não é relevante. Bottom line: você simplesmente não deveria ter mencionado as seis strings criadas por i.ToString(); eles não fazem parte da comparação de eficiência.
Home
12

Sim, StringBuilderoferece melhor desempenho ao executar operações repetidas em uma string. Isso ocorre 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. sob Systemespaço para nome
    2. instância imutável (somente leitura)
    3. desempenho diminui quando ocorre mudança contínua de valor
    4. discussão segura
  • StringBuilder (sequência mutável)

    1. sob System.Textespaço para nome
    2. instância mutável
    3. mostra melhor desempenho, pois novas alterações são feitas na instância existente

Artigo altamente recomendado do dotnet mob: String vs. StringBuilder in C # .

Pergunta de estouro de pilha relacionado: Mutabilidade da string quando a string não muda em C #? .

Shamseer K
fonte
12

String Vs String Builder:

A primeira coisa que você precisa saber é: em que assembléia essas duas classes vivem?

Assim,

string está presente no Systemespaço para nome.

e

StringBuilder está presente no System.Textespaço para nome.

Para declaração de string :

Você precisa incluir o Systemespaço para nome. algo assim. Using System;

e

Para declaração StringBuilder :

Você precisa incluir o System.textespaço para nome. algo assim. Using System.text;

Agora venha a pergunta real.

Qual é a diferença entre string e StringBuilder ?

A principal diferença entre esses dois é que:

string é imutável.

e

StringBuilder é mutável.

Então agora vamos discutir a diferença entre imutável e mutável

Mutável:: significa Mutável.

Imutável:: significa Não Alterável.

Por exemplo:

using System;

namespace StringVsStrigBuilder
{
    class Program
    {
        static void Main(string[] args)
        {
            // String Example

            string name = "Rehan";
            name = name + "Shah";
            name = name + "RS";
            name = name + "---";
            name = name + "I love to write programs.";

            // Now when I run this program this output will be look like this.
            // output : "Rehan Shah RS --- I love to write programs."
        }
    }
}

Então, neste caso, vamos mudar o mesmo objeto 5 vezes.

Então a pergunta óbvia é essa! O que realmente acontece sob o capô, quando trocamos a mesma corda 5 vezes.

É o que acontece quando mudamos a mesma string 5 vezes.

vamos olhar para a figura.

insira a descrição da imagem aqui

Explicação:

Quando inicializamos essa variável "name" para "Rehan", ou seja, string name = "Rehan" essa variável é criada na pilha "name" e aponta para o valor "Rehan". depois que esta linha é executada: "name = name +" Shah ". a variável de referência não está mais apontando para o objeto" Rehan ", agora está apontando para" Shah "e assim por diante.

O mesmo stringvale para o significado imutável de que, uma vez que criamos o objeto na memória, não podemos alterá-lo.

Então, quando concatenamos a namevariável, o objeto anterior permanece lá na memória e outro objeto de cadeia é criado ...

Portanto, a partir da figura acima, temos cinco objetos, os quatro objetos são jogados fora e não são usados. Eles ainda permanecem na memória e ocupam a quantidade de memória. "Garbage Collector" é responsável por isso, para limpar os recursos da memória.

Portanto, no caso de string a qualquer momento, quando manipulamos a string repetidamente, temos muitos objetos Criados e permanecemos lá na memória.

Então essa é a história da string Variable.

Agora vamos olhar para o objeto StringBuilder. Por exemplo:

using System;
using System.Text;

namespace StringVsStrigBuilder
{
    class Program
    {
        static void Main(string[] args)
        {
            // StringBuilder Example

            StringBuilder name = new StringBuilder();
            name.Append("Rehan");
            name.Append("Shah");
            name.Append("RS");
            name.Append("---");
            name.Append("I love to write programs.");


            // Now when I run this program this output will be look like this.
            // output : "Rehan Shah Rs --- I love to write programs."
        }
    }
}

Então, neste caso, vamos mudar o mesmo objeto 5 vezes.

Então a pergunta óbvia é essa! O que realmente acontece sob o capô, quando mudamos o mesmo StringBuilder 5 vezes.

É o que acontece quando alteramos o mesmo StringBuilder 5 vezes.

vamos olhar para a figura. insira a descrição da imagem aqui

Explicação: No caso do objeto StringBuilder. você não receberia o novo objeto. O mesmo objeto será alterado na memória, portanto, mesmo que você altere o objeto e digamos 10.000 vezes, ainda teremos apenas um objeto stringBuilder.

Você não possui muitos objetos de lixo ou objetos string_uilder não referenciados porque pode ser alterado. É mutável, ou seja, muda ao longo do tempo?

Diferenças:

  • String está presente no espaço para nome do sistema, como Stringbuilder presente no espaço para nome System.Text.
  • string é imutável onde StringBuilder é mutável.
Rehan Shah
fonte
8

StringBuilder reduz o número de alocações e atribuições, com um custo de memória extra usada. Utilizado corretamente, ele pode remover completamente a necessidade do compilador alocar cadeias 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
Moswald
fonte
5

O desempenho de uma operação de concatenação para um objeto String ou StringBuilder depende da frequência com que ocorre uma alocação de memória. Uma operação de concatenação de String sempre aloca memória, enquanto uma operação de concatenação de StringBuilder somente aloca memória se o buffer do objeto StringBuilder for muito pequeno para acomodar os novos dados. Conseqüentemente, a classe String é preferível para uma operação de concatenação se um número fixo de objetos String for concatenado. Nesse caso, as operações de concatenação individuais podem até ser combinadas em uma única operação pelo compilador. Um objeto StringBuilder é preferível para uma operação de concatenação se um número arbitrário de seqüências de caracteres for concatenado; por exemplo, se um loop concatena um número aleatório de strings de entrada do usuário.

Fonte: MSDN

user487069
fonte
3

StringBuilder é melhor para criar uma sequência a partir de muitos valores não constantes.

Se você estiver construindo uma sequência a partir de muitos valores constantes, como várias linhas de valores em um documento HTML ou XML ou outros trechos de texto, poderá anexar apenas a mesma sequência, porque quase todos os compiladores fazem isso. "dobragem constante", um processo de redução da árvore de análise quando você tem um monte de manipulação constante (também é usado quando você escreve algo parecido int minutesPerYear = 24 * 365 * 60). E para casos simples com valores não constantes anexados um ao outro, o compilador .NET reduzirá seu código para algo semelhante ao que StringBuilderfaz.

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

JasonTrue
fonte
2

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

Gilligan
fonte
2

No .NET, o StringBuilder ainda é mais rápido que o acréscimo de cadeias. Tenho certeza de que, em Java, eles apenas criam um StringBuffer sob o capô quando você anexa strings, então não há realmente nenhuma diferença. Não sei por que eles ainda não fizeram isso no .NET.

Eric Z Beard
fonte
2

O uso de seqüências de caracteres para concatenação pode levar a uma complexidade de tempo de execução na ordem de O(n^2).

Se você usar a StringBuilder, há muito menos cópia da memória que precisa ser feita. Com oStringBuilder(int capacity) você pode aumentar o desempenho se puder estimar o tamanho da final String. Mesmo se você não for preciso, provavelmente precisará aumentar a capacidade StringBuilderalgumas vezes, o que também pode ajudar no desempenho.

Steve g
fonte
2

Vi ganhos de desempenho significativos ao usar a EnsureCapacity(int capacity)chamada de método em uma instância de StringBuilderantes de usá-la para qualquer armazenamento de string. Eu costumo chamar isso na linha de código após a instanciação. Tem o mesmo efeito como se você instanciasse o StringBuilderseguinte:

var sb = new StringBuilder(int capacity);

Essa chamada aloca a memória necessária antecipadamente, o que causa menos alocações de memória durante várias Append()operações. Você precisa adivinhar qual a quantidade de memória necessária, mas para a maioria dos aplicativos isso não deve ser muito difícil. Geralmente erro do lado de muita memória (estamos falando 1k ou mais).

Jason Jackson
fonte
Não há necessidade de ligar EnsureCapacitylogo após a StringBuilderinstanciação. Basta instanciar o StringBuilderassim: var sb = new StringBuilder(int capacity).
David Ferenczy Rogožan 16/03/16
Foi-me dito que isso ajuda a usar um número primo.
Karl Gjertsen
1

Além das respostas anteriores, a primeira coisa que sempre faço ao pensar em problemas como esse é criar um pequeno aplicativo de teste. Dentro deste aplicativo, realize algum teste de sincronismo nos dois cenários e veja por si mesmo qual é o mais rápido.

IMHO, anexando mais de 500 entradas de string deve definitivamente usar o StringBuilder.

RichS
fonte
1

String e StringBuilder são realmente imutáveis, o StringBuilder possui buffers que permitem gerenciar seu tamanho com mais eficiência. Quando o StringBuilder precisa ser redimensionado é quando é re-alocado no heap. Por padrão, ele é dimensionado para 16 caracteres, você pode configurá-lo no construtor.

por exemplo.

StringBuilder sb = novo StringBuilder (50);

capgpilk
fonte
2
Não sabe ao certo o que significa imutável. As strings são imutáveis, NÃO PODEM ser alteradas. Qualquer coisa "alterada" é apenas que o valor antigo permanece na pilha sem QUALQUER ponteiro para localizá-lo. StringBuilder (mutável) é o tipo de referência no heap, o ponteiro para o heap é alterado e a alocação de espaço para essa alteração.
Tom Stickel
1

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

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

raffimd
fonte
0

StringBuilder é provavelmente preferível. O motivo é que ele aloca mais espaço do que o necessário atualmente (você define o número de caracteres) para deixar espaço para anexos futuros. Os anexos futuros que se encaixam no buffer atual não exigem nenhuma alocação de memória ou coleta de lixo, o que pode ser caro. Em geral, eu uso o StringBuilder para concatentação de cadeias complexas ou formatação múltipla, depois converto para uma String normal quando os dados estão completos e quero um objeto imutável novamente.

considerar
fonte
0

Se você estiver fazendo muita concatenação de cadeias, use um StringBuilder. Ao concatenar com uma String, você cria uma nova String a cada vez, usando mais memória.

Alex

Alex Fort
fonte
0

Como regra geral, se eu tiver que definir o valor da sequência mais de uma vez ou se houver anexos à sequência, será necessário que ele seja um construtor de sequência. Eu já vi aplicativos que escrevi no passado antes de aprender sobre construtores de strings que tiveram uma enorme pegada de memória que parece continuar crescendo e crescendo. Alterar esses programas para usar o construtor de strings reduz significativamente o uso de memória. Agora juro pelo construtor de cordas.

user13288
fonte
0

Minha abordagem sempre foi usar o StringBuilder ao concatenar 4 ou mais seqüências de caracteres OU Quando não sei como podem ocorrer as concatenações.

Artigo relacionado ao bom desempenho aqui

Peter Mortensen
fonte
0

StringBuilder é significativamente mais eficiente, mas você não verá esse desempenho, a menos que esteja realizando uma grande quantidade de modificações em cadeias.

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

Como você pode ver, as 200.000 iterações levaram 22 segundos, enquanto as 1 milhão de iterações que utilizavam o StringBuilderforam quase instantâneas.

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:

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

String concluída + em 28/01/2013 16:55:40.

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

String concluída + em 28/01/2013 16:56:02.

O início do Sb é anexado em 28/01/2013 16:56:02.

Anexo Sb concluído em 28/01/2013 16:56:02.

CathalMF
fonte
1
Se o construtor String é tão poderoso, então por que temos String existente? Por que não eliminamos completamente a String. Eu fiz esta pergunta de modo que você pode me dizer o benefício de corda sobre StringBuilder
Unbreakable
-2

O StringBuilder terá um desempenho melhor, do ponto de vista da memória. Quanto ao processamento, a diferença no tempo de execução pode ser insignificante.

DevelopingChris
fonte