String vs. StringBuilder


215

Capisco la differenza tra Stringe StringBuilder( StringBuilderessendo mutabile) ma c'è una grande differenza di prestazioni tra i due?

Il programma a cui sto lavorando ha molti aggiunte di stringhe basate su maiuscole (500+). Sta usando StringBuilderuna scelta migliore?

Risposte:


236

Sì, la differenza di prestazioni è significativa. Vedi l'articolo KB " Come migliorare le prestazioni di concatenazione di stringhe in Visual C # ".

Ho sempre cercato di programmare prima la chiarezza e poi di ottimizzare le prestazioni in seguito. È molto più facile che farlo al contrario! Tuttavia, dopo aver visto l'enorme differenza di prestazioni nelle mie applicazioni tra i due, ora ci penso un po 'più attentamente.

Fortunatamente, è relativamente semplice eseguire l'analisi delle prestazioni sul codice per vedere dove stai spendendo il tempo e quindi modificarlo per usarlo StringBuilderdove necessario.


44
Una buona regola empirica è usare le stringhe quando non hai intenzione di cambiarlo e usare StringBuilder se lo cambierai.
Tim

31
Mi piace molto questa risposta, soprattutto il consiglio di programmare per chiarezza prima dell'esibizione. Come sviluppatori, dedichiamo tanto o più tempo a leggere il codice mentre lo scriviamo.
Scott Lawrence,

Fuorilegge: penso che dovrebbe diventare la sua risposta votata separatamente, se capisco StackOverflow correttamente.
Jay Bazuzi,

2
sulla base della mia esperienza se stai cercando di concatenare circa 10-15 corde, puoi andare con una stringa ma se no. di stringhe sono più di questo, quindi usa il generatore di stringhe
Peeyush

3
Dipende ancora da come lo si usa, per fare riferimento ai test realmente mi piace Coding Horror - The Sad Tragedy of Micro-Optimization Theater
Erik Philips

56

Per chiarire cosa ha detto Gillian a proposito di 4 corde, se hai qualcosa del genere:

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

allora sarebbe più veloce usando le stringhe e l'operatore più. Questo perché (come Java, come sottolinea Eric), utilizza internamente StringBuilder automaticamente (in realtà, utilizza una primitiva che utilizza anche StringBuilder)

Tuttavia, se quello che stai facendo è più vicino a:

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

Quindi è necessario utilizzare esplicitamente un StringBuilder. .Net non crea automaticamente un StringBuilder qui, perché sarebbe inutile. Alla fine di ogni riga, "a" deve essere una stringa (immutabile), quindi dovrebbe creare e disporre un StringBuilder su ogni riga. Per la velocità, dovresti usare lo stesso StringBuilder fino a quando non hai finito di costruire:

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

5
Non c'è motivo per cui il compilatore C # debba trattare il secondo campione in modo diverso dal primo. In particolare non vi è alcun obbligo di produrre una stringa alla fine di ogni riga. Il compilatore può comportarsi come dici tu, ma non ha l'obbligo di farlo.
CodesInChaos,

@CodesInChaos, una variabile di stringa immutabile viene specificatamente assegnata alla fine di ogni riga, non crea l'obbligo di produrre una stringa? Tuttavia, sono d'accordo sul fatto che non c'è motivo di trattare ogni singola linea in modo diverso (e non sono sicuro che lo sia), tuttavia la perdita di prestazioni deriva dalla riallocazione, quindi non avrebbe importanza.
Saeb Amini,

@SaebAmini - Se aè una variabile locale e l'oggetto a cui fa riferimento non è stato assegnato a qualche altra variabile (che potrebbe essere accessibile a un altro thread), un buon ottimizzatore potrebbe determinare che anon è accessibile da nessun altro codice durante questa sequenza di Linee; solo il valore finale delle acose. Quindi potrebbe trattare queste tre righe di codice come se fossero state scritte a = b + c + d;.
ToolmakerSteve

29

StringBuilder è preferibile SE stai eseguendo più loop o forchette nel tuo pass di codice ... tuttavia, per prestazioni PURE, se riesci a cavartela con un SINGOLO dichiarazione di stringa, allora è molto più performante.

Per esempio:

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

è più performante di

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..

In questo caso, StringBuild potrebbe essere considerato più gestibile, ma non è più performante della singola dichiarazione di stringa.

9 volte su 10 però ... usa il generatore di stringhe.

Nota a margine: string + var è anche più performante dell'approccio string.Format (generalmente) che usa internamente uno StringBuilder (in caso di dubbi ... controlla il riflettore!)


17
Vorrei che avessi detto come lo sai / come verificarlo.
ChrisW,

2
Non si verificano le prestazioni nel riflettore: si verifica le prestazioni temporizzando il codice di rilascio, si analizza con un profiler e si cerca una spiegazione con il riflettore.
Albin Sunnanbo,

3
Prendi in considerazione l'utilizzo di String.Format () per concatenare un piccolo numero di stringhe di piccole dimensioni, in particolare se l'obiettivo è la formattazione dei messaggi per mostrare all'utente.
Gary Kindel,

1
Questa è una pessima informazione. ("string myString è più performante") non è affatto vero.
Tom Stickel,

3
Le informazioni in questa risposta non sono corrette. StringBuilder è un po 'più veloce della concatenazione eseguita nella stessa istruzione; tuttavia, noterai una differenza tra i due solo se lo fai centinaia di migliaia di volte ( Fonte ). Come detto nella fonte, "non importa!"
David Sherret,

25

Un semplice esempio per dimostrare la differenza di velocità quando si utilizza la Stringconcatenazione rispetto a 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");

Risultato:

Utilizzo della concatenazione di stringhe: 15423 millisecondi

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");

Risultato:

Utilizzando StringBuilder: 10 millisecondi

Di conseguenza, la prima iterazione impiegava 15423 ms mentre la seconda iterazione usando StringBuilderimpiegava 10 ms.

Mi sembra che l'utilizzo StringBuildersia più veloce, molto più veloce.


24

Questo benchmark mostra che la concatenazione regolare è più veloce quando si combinano 3 o meno stringhe.

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

StringBuilder può apportare un miglioramento molto significativo nell'utilizzo della memoria, specialmente nel caso in cui si sommino 500 stringhe.

Considera il seguente esempio:

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

Cosa succede nella memoria? Vengono create le seguenti stringhe:

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"

Aggiungendo quei cinque numeri alla fine della stringa abbiamo creato 13 oggetti stringa! E 12 di loro erano inutili! Wow!

StringBuilder risolve questo problema. Non è una "stringa mutabile" come spesso sentiamo ( tutte le stringhe in .NET sono immutabili ). Funziona mantenendo un buffer interno, un array di caratteri. Chiamare Append () o AppendLine () aggiunge la stringa allo spazio vuoto alla fine dell'array char; se l'array è troppo piccolo, crea un nuovo array più grande e copia lì il buffer. Quindi nell'esempio sopra, StringBuilder potrebbe aver bisogno di un solo array per contenere tutte e 5 le aggiunte alla stringa, a seconda della dimensione del suo buffer. Puoi dire a StringBuilder quanto deve essere grande il suo buffer nel costruttore.


2
Minor nit: è un po 'strano dire "abbiamo creato 13 oggetti stringa e 12 di questi erano inutili", quindi dire StringBuilder risolve questo problema. Dopotutto, sei delle stringhe che non hai altra scelta che creare; vengono da i.ToString(). Quindi con StringBuilder, devi ancora creare 6 + 1 stringhe; ha ridotto la creazione di 13 stringhe alla creazione di 7 stringhe. Ma quello è ancora il modo sbagliato di guardarlo; la creazione delle sei stringhe numeriche non è rilevante. In conclusione: semplicemente non avresti dovuto menzionare le sei stringhe create da i.ToString(); non fanno parte del confronto di efficienza.
ToolmakerSteve

12

Sì, StringBuilderoffre prestazioni migliori durante l'esecuzione di operazioni ripetute su una stringa. È perché tutte le modifiche vengono apportate a una singola istanza in modo da poter risparmiare molto tempo invece di creare una nuova istanza comeString .

String Vs Stringbuilder

  • String

    1. sotto System spazio dei nomi
    2. istanza immutabile (sola lettura)
    3. le prestazioni diminuiscono quando si verifica una variazione continua del valore
    4. filo sicuro
  • StringBuilder (stringa mutabile)

    1. sotto System.Text spazio dei nomi
    2. istanza mutabile
    3. mostra prestazioni migliori poiché vengono apportate nuove modifiche all'istanza esistente

Consiglio vivamente l'articolo mob dotnet: String Vs StringBuilder in C # .

Domanda di overflow dello stack correlata: Mutabilità della stringa quando la stringa non cambia in C #? .


12

String Vs String Builder:

Prima cosa che devi sapere che in quale assemblea vivono queste due classi?

Così,

stringa è presente nello Systemspazio dei nomi.

e

StringBuilder è presente nello System.Textspazio dei nomi.

Per la dichiarazione di stringa :

Devi includere lo Systemspazio dei nomi. qualcosa come questo. Using System;

e

Per la dichiarazione StringBuilder :

Devi includere lo System.textspazio dei nomi. qualcosa come questo. Using System.text;

Ora vieni la vera domanda.

Qual è la differenza tra string e StringBuilder ?

La differenza principale tra questi due è che:

la stringa è immutabile.

e

StringBuilder è modificabile.

So Now consente di discutere la differenza tra immutabile e mutevole

Mutabile: significa modificabile.

Immutabile: significa non modificabile.

Per esempio:

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."
        }
    }
}

Quindi, in questo caso, cambieremo lo stesso oggetto 5 volte.

Quindi la domanda ovvia è quella! Cosa succede effettivamente sotto il cofano, quando cambiamo la stessa stringa 5 volte.

Questo è ciò che accade quando cambiamo la stessa stringa 5 volte.

guardiamo la figura.

inserisci qui la descrizione dell'immagine

spiegazione:

Quando inizializziamo per la prima volta questa variabile "nome" su "Rehan", ad es string name = "Rehan" questa variabile viene creata nello stack "nome" e punta a quel valore "Rehan". dopo che questa riga è stata eseguita: "name = name +" Shah ". la variabile di riferimento non punta più a quell'oggetto" Rehan "ora punta a" Shah "e così via.

Quindi stringè immutabile il significato che una volta creato l'oggetto nella memoria non possiamo cambiarlo.

Quindi quando concateniamo la namevariabile l'oggetto precedente rimane lì nella memoria e viene creato un altro nuovo oggetto stringa ...

Quindi dalla figura sopra abbiamo cinque oggetti i quattro oggetti che vengono gettati via non vengono usati affatto. Rimangono nella memoria e occultano la quantità di memoria. "Garbage Collector" è responsabile di ciò così pulito che le risorse dalla memoria.

Quindi in caso di stringa ogni volta che manipoliamo la stringa più e più volte abbiamo alcuni oggetti creati e rimaniamo lì nella memoria.

Quindi questa è la storia della stringa Variable.

Ora diamo un'occhiata all'oggetto StringBuilder. Per esempio:

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."
        }
    }
}

Quindi, in questo caso, cambieremo lo stesso oggetto 5 volte.

Quindi la domanda ovvia è quella! Cosa succede realmente sotto il cofano, quando cambiamo lo stesso StringBuilder 5 volte.

Questo è ciò che accade quando cambiamo lo stesso StringBuilder 5 volte.

guardiamo la figura. inserisci qui la descrizione dell'immagine

Spiegazione: in caso di oggetto StringBuilder. non otterrai il nuovo oggetto. Lo stesso oggetto cambierà in memoria, quindi anche se si cambia l'oggetto e diciamo 10.000 volte avremo ancora un solo oggetto stringBuilder.

Non hai molti oggetti garbage o oggetti stringBuilder non referenziati perché possono essere cambiati. È mutevole, nel senso che cambia nel tempo?

differenze:

  • String è presente nello spazio dei nomi di sistema dove Stringbuilder è presente nello spazio dei nomi System.Text.
  • string è immutabile laddove StringBuilder è mutabe.

8

StringBuilder riduce il numero di allocazioni e assegnazioni, a un costo aggiuntivo di memoria utilizzata. Se usato correttamente, può rimuovere completamente la necessità che il compilatore assegni continuamente stringhe sempre più grandi fino a quando non viene trovato il risultato.

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

5

Le prestazioni di un'operazione di concatenazione per un oggetto String o StringBuilder dipendono dalla frequenza con cui si verifica un'allocazione di memoria. Un'operazione di concatenazione di stringhe alloca sempre la memoria, mentre un'operazione di concatenazione di StringBuilder alloca memoria solo se il buffer degli oggetti StringBuilder è troppo piccolo per contenere i nuovi dati. Di conseguenza, la classe String è preferibile per un'operazione di concatenazione se viene concatenato un numero fisso di oggetti String. In tal caso, le singole operazioni di concatenazione potrebbero anche essere combinate in un'unica operazione dal compilatore. Un oggetto StringBuilder è preferibile per un'operazione di concatenazione se viene concatenato un numero arbitrario di stringhe; ad esempio, se un ciclo concatena un numero casuale di stringhe di input dell'utente.

Fonte: MSDN


3

StringBuilder è meglio per creare una stringa da molti valori non costanti.

Se stai costruendo una stringa da molti valori costanti, come più righe di valori in un documento HTML o XML o altri blocchi di testo, puoi evitare di aggiungere semplicemente la stessa stringa, perché quasi tutti i compilatori lo fanno "ribaltamento costante", un processo di riduzione dell'albero di analisi quando si ha un mucchio di manipolazioni costanti (viene anche utilizzato quando si scrive qualcosa di simile int minutesPerYear = 24 * 365 * 60). E per casi semplici con valori non costanti aggiunti l'uno all'altro, il compilatore .NET ridurrà il codice a qualcosa di simile a quello che StringBuilderfa.

Ma quando il compilatore non può ridurre la tua aggiunta a qualcosa di più semplice, ti consigliamo di StringBuilder. Come sottolinea Fizch, è più probabile che accada all'interno di un ciclo.


2

Credo che StringBuilder sia più veloce se hai più di 4 stringhe che devi aggiungere insieme. Inoltre può fare cose interessanti come AppendLine.


2

In .NET, StringBuilder è ancora più veloce dell'aggiunta di stringhe. Sono abbastanza sicuro che in Java, creino un StringBuffer sotto il cofano quando aggiungi le stringhe, quindi non c'è davvero alcuna differenza. Non sono sicuro del perché non lo abbiano ancora fatto in .NET.



2

L'uso di stringhe per la concatenazione può portare a una complessità di runtime nell'ordine di O(n^2).

Se si utilizza a StringBuilder, è necessario copiare molto meno la memoria. Con questo StringBuilder(int capacity)puoi aumentare le prestazioni se puoi stimare quanto sarà grande la finale String. Anche se non sei preciso, probabilmente dovrai solo aumentare la capacità di StringBuilderun paio di volte, il che può aiutare anche le prestazioni.


2

Ho visto significativi miglioramenti delle prestazioni usando la EnsureCapacity(int capacity)chiamata del metodo su un'istanza di StringBuilderprima di usarla per qualsiasi archiviazione di stringhe. Di solito lo chiamo sulla riga di codice dopo l'istanza. Ha lo stesso effetto di un'istanza StringBuildercome questa:

var sb = new StringBuilder(int capacity);

Questa chiamata alloca la memoria necessaria in anticipo, il che provoca meno allocazioni di memoria durante più Append()operazioni. Devi fare un'ipotesi ponderata su quanta memoria ti servirà, ma per la maggior parte delle applicazioni questo non dovrebbe essere troppo difficile. Di solito sbaglio sul lato di un po 'troppo di memoria (stiamo parlando 1k o giù di lì).


Non è necessario chiamare EnsureCapacitysubito dopo l' StringBuilderistanza. Semplicemente un'istanza StringBuilderin questo modo: var sb = new StringBuilder(int capacity).
David Ferenczy Rogožan,

Mi è stato detto che aiuta ad usare un numero primo.
Karl Gjertsen,

1

Oltre alle risposte precedenti, la prima cosa che faccio sempre quando penso a problemi come questo è creare una piccola applicazione di prova. All'interno di questa app, esegui alcuni test di temporizzazione per entrambi gli scenari e vedi di persona quale è più veloce.

IMHO, l'aggiunta di oltre 500 voci di stringa dovrebbe sicuramente utilizzare StringBuilder.


1

String e StringBuilder sono in realtà entrambi immutabili, StringBuilder ha buffer integrati che consentono di gestire le sue dimensioni in modo più efficiente. Quando StringBuilder deve essere ridimensionato è quando viene riassegnato sull'heap. Di default ha una dimensione di 16 caratteri, puoi impostarlo nel costruttore.

per esempio.

StringBuilder sb = new StringBuilder (50);


2
Non sei sicuro di capire cosa significhi immutabile. Le stringhe sono immutabili, NON POSSONO essere cambiate. Qualunque cosa "modificata" è in realtà solo che il vecchio valore rimane nell'heap senza NESSUN puntatore per individuarlo. StringBuilder (mutabile) è il tipo di riferimento sull'heap, il puntatore all'heap viene modificato e l'allocazione dello spazio per questa modifica.
Tom Stickel,

1

La concatenazione di stringhe ti costerà di più. In Java, è possibile utilizzare StringBuffer o StringBuilder in base alle proprie esigenze. Se vuoi un'implementazione sincronizzata e sicura dei thread, scegli StringBuffer. Questo sarà più veloce della concatenazione di stringhe.

Se non hai bisogno dell'implementazione sincronizzata o sicura dei thread, scegli StringBuilder. Questo sarà più veloce della concatenazione di stringhe e anche più veloce di StringBuffer poiché il loro non è un sovraccarico di sincronizzazione.


0

StringBuilder è probabilmente preferibile. Il motivo è che alloca più spazio del necessario (si imposta il numero di caratteri) per lasciare spazio a futuri accodamenti. Quindi quei futuri accodamenti che si adattano al buffer corrente non richiedono alcuna allocazione di memoria o garbage collection, che può essere costoso. In generale, utilizzo StringBuilder per concatenazioni di stringhe complesse o formattazioni multiple, quindi converto in una stringa normale quando i dati sono completi e desidero di nuovo un oggetto immutabile.


0

Se stai eseguendo molte concatenazioni di stringhe, utilizza StringBuilder. Quando si concatena con una stringa, si crea una nuova stringa ogni volta, utilizzando più memoria.

alex


0

Come regola generale, se devo impostare il valore della stringa più di una volta o se ci sono aggiunte alla stringa, allora deve essere un generatore di stringhe. Ho visto applicazioni che ho scritto in passato prima di conoscere costruttori di stringhe che hanno avuto un'enorme impronta di memoria che sembra continuare a crescere e crescere. La modifica di questi programmi per utilizzare il generatore di stringhe riduce notevolmente l'utilizzo della memoria. Ora lo giuro sul costruttore di stringhe.



0

StringBuilder è significativamente più efficiente ma non vedrai quella prestazione a meno che non stia facendo una grande quantità di modifica della stringa.

Di seguito è riportato un breve pezzo di codice per dare un esempio delle prestazioni. Come puoi vedere, inizi a vedere un notevole aumento delle prestazioni solo quando entri in grandi iterazioni.

Come puoi vedere, le 200.000 iterazioni impiegarono 22 secondi mentre le 1 milione di iterazioni che utilizzavano StringBuildererano quasi istantanee.

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();

Risultato del codice sopra:

Inizio stringa + al 28/01/2013 alle 16:55:40.

Corda finita + al 28/01/2013 alle 16:55:40.

Inizio stringa + al 28/01/2013 alle 16:55:40.

Corda finita + al 28/01/2013 alle 16:56:02.

A partire da Sb append al 28/01/2013 alle 16:56:02.

Appendice Sb terminata il 28/01/2013 alle 16:56:02.


1
Se String Builder è così potente, allora perché abbiamo String in esistenza. Perché non cancelliamo completamente String. Ho fatto questa domanda in modo che tu possa dirmi il VANTAGGIO di String over StringBuilder
Unbreakable

-2

StringBuilder avrà prestazioni migliori, dal punto di vista della memoria. Per quanto riguarda l'elaborazione, la differenza nel tempo di esecuzione può essere trascurabile.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.