StringBuilder - czemu i kiedy używać

0

Słyszałem że warto używać tej klasy zamist stringa gdy będziemy robić operacje na danym tekście.
Ale kiedy warto stosować? Powyżej ilu operacji?
Bo niby to oszczędza pamięć jako że dodając do stringa tworzymy jego kopię.
Ale jak mamy taki program

 StringBuilder sb = new StringBuilder();
for (int i = 0 ; i < 100 ; i++)
{
sb.AppendLine("znak " + i); //tu za każdym razem tworzymy string "znak" oraz string "znak" + i 
Console.Clear();
Console.WriteLine(sb.ToString()); //tu konwertujemy sb do stringa czyli tworzymy znowu obiekt typu string!
}

Oczywiście program nie ma żadnego znaczenia, chciałem tylko pokazać o co mi chodzi. Zdaje mi się że podczas operacji na StringBuilderze tworzymy i tak masę niepotrzebnych stringów. Potem pokazując zawartość SB'ka znowu tworzymy obiekt typu string. Przecież to istny bałagan. Nie mniejszy chyba niż ze string += dane.
Czy mógłby mi ktoś wytłumaczyć kiedy i dlaczego i jak warto używać SB'ka?

0

Ale kiedy warto stosować? Powyżej ilu operacji?
kiedyś czytałem że powyżej czterech, a to było na blogu pracownika Microsoftu, który zajmował się .netem.
Możesz zrobić mały benchmark, zmierzyć która klasa działa szybciej w danym przypadku.

Generalnie jest tak, że StringBuilder powinien być zawsze szybszy, gdyż nie tworzy całkiem nowego stringa przy każdej operacji. Jednak kompilator traktuje typ String specjalnie, i stosuje dla niego specjalne optymalizacje (a dla StringBuildera nie).

W przykładzie który podałeś, SB jest jak najbardziej uzasadniony. W przykładzie typu:

string s = "ala ma " + n.ToString() + " kotów"

lepiej zostawić po prostu string.

0

Generalnie jest tak, że StringBuilder powinien być zawsze szybszy, gdyż nie tworzy całkiem nowego stringa przy każdej operacji. Jednak kompilator traktuje typ String specjalnie, i stosuje dla niego specjalne optymalizacje (a dla StringBuildera nie).

Nie do końca, StringBuilder alokuje sobie po prostu nadmiarowy bufor żeby móc szybko do niego dopisywać. StringBuilder ma (wg. panów z Microsoftu) specjalne traktowanie i nie da się napisać swojej klasy działającej równie szybko.

W przykładzie który podałeś, StringBuilder jest jak najbardziej uzasadniony. W przykładzie typu:

string s = "ala ma " + n.ToString() + " kotów"

lepiej napisać

string s = string.Format("ala ma {0} kotów", n)" // :P

ale to już nie dla wydajności tylko dla pięknego kodu.

Ja czytałem że warto stosować od około ośmiu operacji. Ale to wszystko zależy od wielkości łączonych stringów etc, etc, etc. Należy się bawić z tym dopiero wtedy kiedy ma to znaczący wpływ na wydajność (na przykład generowanie długie tekstu, ale dodanie kilku stringów jednorazowo po tym jak użytkownik kliknie na przycisk oczywiście nie).

1

String w C# (i w Javie) jest immutable, każda zmian powoduje przepisanie w nowe miejsce.
W Javie kod

        StringBuilder zwrot=new StringBuilder("");
        for (int i=1;i<=300000;i++)
        {
            zwrot.append("A");
        }

jest ponad 32 tysiące razy szybszy niż kod

        String zwrot="";
        for (int i=1;i<=300000;i++)
        {
            zwrot+="A";
        }
0

Słusznie zauważyłeś, że w twoim przykładzie i tak tworzysz 100 nowych stringów, dlatego nie powinieneś tam używać konkatenacji stringów, ale wszystko appendować:

sb.Append("znak ").Append(i).AppendLine();

W przypadku Javy, w wielu IDE można włączyć, by zwykła konkatenacja wewnątrz append() powodowała ostrzeżenie.

I wcale nie jest tak, że za każdym razem tworzysz stringa "znak " - jest on przechowywany w puli stringów i za każdym razem odwołujesz się do tego samego obiektu.

0

Szybkość szybkością a jak z pamięcią? Czy da się jakoś usuwać starą kopię stringa przy operacji
string zwrot = "";
zwrot += "dupa";
Czy w ogóle typy referencyjne da się jakoś usuwać? Wiem że jest interfejs IDisposable ale jak powinien w takim razie wyglądać kod zwalniający pamięć w funkcji Dispose?

0

Interfejs IDisposable służy do usuwania obiektów z pamięci niezarządzanej oraz zwalniania niezarządzanych uchwytów.

O czymś takim jak Garbage Collector słyszałeś?

0

A czy takie wyrażenie

Console.WriteLine("tekst {0}", a);

jest lepsze aniżeli

Console.WriteLine("tekst " + a);

I czy wyrażenie

StringBuilder esbek = new StringBuilder(); 
double d = 123.3;
esbek.Append(d.ToString());

jest identyczne co

StringBuilder esbek = new StringBuilder(); 
double d = 123.3;
esbek.Append(d);

Nie pytam oczywiście o kwestie estetyczne

0

Mam ważne pytanie.
Piszę sobię w międzyczasie aplikację ala telnet. No i produkuje ona ogromne ilości tekstu, po połączeniu np z serwerem IRC.
Korzystam z kontrolki RichTextBox, której tekst jak wiadomo można dodać za pomocą richTextBox.Text += tekst.
Pewnie nie jest to słuszne rozwiązanie? Jak mógłbym to inaczej zrobić? Tak czy siak wiadomość musi wyjść na ekran, w czasie niezbyt późnym od jej dostania. Co proponujecie? Z góry dzięki za pomoc

0

richTextBox1.AppendText("");

0
ubuntuser napisał(a)

A czy takie wyrażenie

Console.WriteLine("tekst {0}", a);

jest lepsze aniżeli

Console.WriteLine("tekst " + a);

To pierwsze jest prawdopodobnie wolniejsze, aczkolwiek ładniejsze i częściej stosowane. Ale tak, jeśli potrzebujesz szybkości, wybierz drugie.

I czy wyrażenie

StringBuilder esbek = new StringBuilder(); 
double d = 123.3;
esbek.Append(d.ToString());

jest identyczne co

StringBuilder esbek = new StringBuilder(); 
double d = 123.3;
esbek.Append(d);

Nie pytam oczywiście o kwestie estetyczne

To trochę bardziej skomplikowane :>. Aceur, na ile go zrozumiałem, podał zupełnie błędną interpretację bo double nigdy nie da się skonwertować, jawnie czy niejawnie, do stringa.

W tym przypadku akurat rozwiązanie jest proste - StringBuilder.Append ma wiele przeciążonych wersji, czyli w jego kodzie znajdują się na przykład metody

public void Append(int i) { }
public void Append(double d) { } // !
public void Append(string s) { }
public void Append(float f) { }

etc. W wyniku tego faktycznie, nie ma prawdopodobnie różnicy w szybkości (a nawet to drugie może być szybsze).

Z drugiej strony, gdyby były tylko metody

public void Append(string s) { } // dołączamy napisy
public void Append(object o) {Append(o.ToString()); } // dołączamy wszystko inne 

To różnica byłaby, i to ogromna (no, może lekko przesadzam).

Dlatego że wykonanie Append(2.0) powodowałoby kolejno:

  • Stworzenie nowego obiektu typu System.Object na stercie
  • Boxing zmiennej (tutaj literału) double do tego obiektu
  • Wywołanie funkcji wirtualnej na strukturze (jedyna możliwość, obok Equals i GetHashCode zrobienia tego, struktury nie mogą definiować własnych funkcji wirtualnych)
  • (Dodatkowe wywołanie funkcji - to akurat strata nieduża i nie wiadomo czy w ogóle takie dodatkowe wywołanie tam jest)
  • W przyszłości - dodatkowa robota dla GC, bo dodatkowy stworzony obiekt trzeba zwolnić.

Myślę że dla niedużych napisów druga wersja może być 2, może 2.5 raza wolniejsza.

Dlatego czasami w miejscach krytycznych dla wydajności trzeba uważać. Rozwiązaniem tego problemu byłoby napisanie Append(2.ToString()) zamiast Append(2).

1 użytkowników online, w tym zalogowanych: 0, gości: 1