Najskuteczniejszy sposób łączenia strun?

Jaki jest najskuteczniejszy sposób łączenia łańcuchów?

Author: Alex Angas, 2008-08-22

17 answers

Metoda StringBuilder.Append() jest znacznie lepsza niż użycie operatora+. Ale odkryłem, że przy wykonywaniu 1000 konkatenacji lub mniej, String.Join() jest nawet bardziej wydajny niż StringBuilder.

StringBuilder sb = new StringBuilder();
sb.Append(someString);

Jedyny problem z String.Join polega na tym, że musisz połączyć łańcuchy ze wspólnym ogranicznikiem. (Edit:) jak zauważył @ryanversaw, możesz utworzyć ciąg ogranicznika.Pusty.

string key = String.Join("_", new String[] 
{ "Customers_Contacts", customerID, database, SessionID });
 131
Author: TheEmirOfGroofunkistan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-12 06:59:16

Rico Mariani , guru wydajności. NET, napisał Artykuł na ten właśnie temat. To nie takie proste, jak można by przypuszczać. Podstawowa rada jest taka:

Jeśli Twój wzór wygląda następująco:

x = f1(...) + f2(...) + f3(...) + f4(...)

To jeden konkat i jest zippy, StringBuilder pewnie nie pomoże.

Jeśli Twój wzór wygląda następująco:

if (...) x += f1(...)
if (...) x += f2(...)
if (...) x += f3(...)
if (...) x += f4(...)

Więc prawdopodobnie chcesz StringBuilder.

Kolejny artykuł na poparcie tego twierdzenia pochodzi od Erica Lipperta, gdzie szczegółowo opisuje optymalizacje wykonywane na jednej linii + konkatenacji.

 243
Author: Lee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-26 04:37:11

Istnieje 6 rodzajów konkatenacji ciągów:

  1. używając symbolu plus (+).
  2. za pomocą string.Concat().
  3. za pomocą string.Join().
  4. za pomocą string.Format().
  5. za pomocą string.Append().
  6. za pomocą StringBuilder.

W eksperymencie udowodniono, że {[1] } jest najlepszym sposobem podejścia, jeśli słowa są mniejsze niż 1000 (w przybliżeniu), a jeśli słowa są większe niż 1000, należy użyć StringBuilder.

Aby uzyskać więcej informacji, sprawdź tę stronę .

String.Join () vs string.Concat ()

Ciąg.Metoda Concat jest tutaj równoważna łańcuchowi znaków.Join wywołanie metody z pustym separatorem. Dodawanie pustego ciągu jest szybkie, ale nie robienie tego jest jeszcze szybsze, więc string.Metoda Concat {[35] } byłaby tutaj lepsza.

 66
Author: Mr_Green,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-26 12:51:12

From Chinh Do-StringBuilder nie zawsze jest szybszy :

Rules of Thumb

  • Podczas łączenia trzech dynamicznych wartości ciągu lub mniej, użyj tradycyjnego ciągu.

  • Podczas łączenia więcej niż trzech dynamicznych wartości łańcuchowych, użyj StringBuilder.

  • Budując duży ciąg znaków z kilku literałów, użyj literała @ string lub operatora inline+.

Większość z czas StringBuilder jest najlepszym rozwiązaniem, ale są przypadki, jak pokazano w tym poście, że powinieneś przynajmniej pomyśleć o każdej sytuacji.

 51
Author: palehorse,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-21 20:34:36

Jeśli działasz w pętli, StringBuilder jest prawdopodobnie dobrym rozwiązaniem; oszczędza ci to narzuty związane z regularnym tworzeniem nowych ciągów. W kodzie, który uruchomi się tylko raz, String.Concat jest prawdopodobnie w porządku.

Jednak Rico Mariani (. NET optimization guru) stworzył quiz , w którym stwierdził na końcu, że w większości przypadków zaleca String.Format.
 11
Author: Adam V,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-21 20:35:39

Z tego artykułu MSDN :

Jest trochę narzutów związanych z tworzenie obiektu StringBuilder, zarówno w czasie i pamięci. Na maszynie z szybka pamięć, StringBuilder staje się warto, jeśli robisz około pięciu szef. Z reguły powiedzmy 10 lub więcej operacji ciągów jest uzasadnieniem dla napowietrznych na każdą maszynę, nawet wolniejszą.

Więc jeśli ufasz MSDN idź z StringBuilder, jeśli musisz zrobić więcej niż 10 strings operations / concatenations-w przeciwnym razie prosty string concat z ' + ' jest w porządku.

 6
Author: JohnIdol,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-22 08:51:12

Oto najszybsza metoda, jaką rozwinąłem w ciągu dekady dla mojej dużej aplikacji NLP. Mam odmiany dla IEnumerable<T> i innych typów wejściowych, z i bez separatorów różnych typów (Char, String), ale tutaj pokazuję prosty przypadek łączenie wszystkich łańcuchów w tablicy w jeden ciąg bez separatora. Najnowsza wersja tutaj jest rozwijana i testowana jednostkowo na C # 7 i . NET 4.7.

Istnieją dwa klucze do wyższej wydajności; najpierw należy wstępnie obliczyć dokładny całkowity wymagany rozmiar. Ten krok jest trywialny, gdy wejście jest tablicą, jak pokazano tutaj. Do obsługi IEnumerable<T> zamiast tego, warto najpierw zebrać łańcuchy do tymczasowej tablicy do obliczenia tej sumy (tablica jest wymagana, aby uniknąć wywołania ToString() więcej niż raz na element, ponieważ technicznie, biorąc pod uwagę możliwość wystąpienia efektów ubocznych, może to zmienić oczekiwaną semantykę operacji 'string join').

Następnie, biorąc pod uwagę całkowitą wielkość przydziału ostatni ciąg, największy wzrost wydajności uzyskuje się przez budowanie ciągu wyników w miejscu. Wymaga to (być może kontrowersyjnej) techniki czasowego zawieszenia niezmienności nowej String, która początkowo jest przydzielana zer. Wszelkie takie kontrowersje na bok, jednak...

...zauważ, że jest to jedyne rozwiązanie zbiorcze na tej stronie, które całkowicie pozwala uniknąć dodatkowej rundy alokacji i kopiowania przez String konstruktor.

Kompletny kod:

/// <summary>
/// Concatenate the strings in 'rg', none of which may be null, into a single String.
/// </summary>
public static unsafe String StringJoin(this String[] rg)
{
    int i;
    if (rg == null || (i = rg.Length) == 0)
        return String.Empty;

    if (i == 1)
        return rg[0];

    String s, t;
    int cch = 0;
    do
        cch += rg[--i].Length;
    while (i > 0);
    if (cch == 0)
        return String.Empty;

    i = rg.Length;
    fixed (Char* _p = (s = new String(default(Char), cch)))
    {
        Char* pDst = _p + cch;
        do
            if ((t = rg[--i]).Length > 0)
                fixed (Char* pSrc = t)
                    memcpy(pDst -= t.Length, pSrc, (UIntPtr)(t.Length << 1));
        while (pDst > _p);
    }
    return s;
}

[DllImport("MSVCR120_CLR0400", CallingConvention = CallingConvention.Cdecl)]
static extern unsafe void* memcpy(void* dest, void* src, UIntPtr cb);

Powinienem wspomnieć, że ten kod ma niewielką modyfikację z tego, czego sam używam. W oryginale wywołuję instrukcję cpblk IL Z C#, aby wykonać rzeczywiste kopiowanie. Dla uproszczenia i przenośności w kodzie tutaj, zastąpiłem to P / Invoke memcpy zamiast, jak widać. Aby uzyskać najwyższą wydajność na x64 (, ale może nie x86) możesz użyć cpblk zamiast metody.

 6
Author: Glenn Slayden,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-10 22:06:05

Dodając do pozostałych odpowiedzi, należy pamiętać, że StringBuilder może podać początkową ilość pamięci do przydzielenia .

Parametr capacity określa maksymalną liczbę znaków, które mogą być przechowywane w pamięci przydzielonej przez bieżącą instancję. Jego wartość jest przypisana do właściwości Capacity . Jeśli liczba znaków, które mają być zapisane w bieżącej instancji, przekracza wartość capacity , obiekt StringBuilder przydziela dodatkowa pamięć do ich przechowywania.

Jeśli pojemność jest równa zeru, używana jest domyślna pojemność specyficzna dla implementacji.

Wielokrotne dodawanie do Stringbuildera, który nie został wcześniej przydzielony, może skutkować wieloma niepotrzebnymi przydziałami, podobnie jak wielokrotne łączenie zwykłych łańcuchów.

Jeśli wiesz, jak długi będzie ostatni ciąg, możesz trywialnie go obliczyć, lub możesz zgadnąć o powszechnym przypadku (przydzielanie zbyt dużej ilości niekoniecznie źle), powinieneś przekazać te informacje konstruktorowi lub właściwości Capacity. szczególnie podczas wykonywania testów wydajności w celu porównania StringBuilder z innymi metodami, takimi jak String.Concat, które robią to samo wewnętrznie. Każdy test, który widzisz online, który nie zawiera wstępnej alokacji StringBuilder w swoich porównaniach, jest błędny.

Jeśli nie potrafisz odgadnąć rozmiaru, prawdopodobnie piszesz funkcję użyteczności, która powinna mieć własną opcjonalny argument do kontrolowania wstępnej alokacji.

 5
Author: DBN,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-31 17:43:24

Ważne jest również, aby zwrócić uwagę na to, że powinieneś użyć operatora +, jeśli łączysz literały łańcuchowe .

Podczas łączenia literałów lub stałych łańcuchów za pomocą operatora+, kompilator tworzy pojedynczy łańcuch. Nie dochodzi do konkatenacji czasu pracy.

Jak: połączyć wiele łańcuchów (C# Programming Guide)

 4
Author: talles,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-14 17:15:53

Następujące może być jeszcze jedno alternatywne rozwiązanie do łączenia wielu łańcuchów.

String str1 = "sometext";
string str2 = "some other text";

string afterConcate = $"{str1}{str2}";

String interpolation

 3
Author: RP Nainwal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-22 21:29:50

Najbardziej efektywnym jest użycie Stringbuildera, w ten sposób:

StringBuilder sb = new StringBuilder();
sb.Append("string1");
sb.Append("string2");
...etc...
String strResult = sb.ToString();

@jonezy: String.Concat jest w porządku, jeśli masz kilka małych rzeczy. Ale jeśli łączysz megabajty danych, twój program prawdopodobnie zatankuje.

 2
Author: TheSmurf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-21 20:28:13

To naprawdę zależy od wzorca użytkowania. Szczegółowy benchmark między ciągiem.Join, string, Concat i string.Format można znaleźć tutaj: String.Format Nie nadaje się do intensywnego logowania

(to jest właściwie ta sama odpowiedź, którą dałem to pytanie)

 1
Author: Liran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:19

System.Ciąg jest niezmienny. Kiedy modyfikujemy wartość zmiennej łańcuchowej, to nowa pamięć jest przydzielana do nowej wartości i zwalnia poprzedni przydział pamięci. System.StringBuilder został zaprojektowany tak, aby miał koncepcję zmiennego ciągu, w którym różne operacje mogą być wykonywane bez przydzielania oddzielnego miejsca pamięci dla zmodyfikowanego ciągu.

 1
Author: Dhibi_Mohanned,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-24 10:27:10

Wypróbuj te 2 kawałki kodu, a znajdziesz rozwiązanie.

 static void Main(string[] args)
    {
        StringBuilder s = new StringBuilder();
        for (int i = 0; i < 10000000; i++)
        {
            s.Append( i.ToString());
        }
        Console.Write("End");
        Console.Read();
    }

Vs

static void Main(string[] args)
    {
        string s = "";
        for (int i = 0; i < 10000000; i++)
        {
            s += i.ToString();
        }
        Console.Write("End");
        Console.Read();
    }

Przekonasz się, że pierwszy kod zakończy się bardzo szybko, a pamięć będzie w dobrej ilości.

Drugi kod może pamięć będzie ok, ale zajmie to dłużej... o wiele dłużej. Więc jeśli masz aplikację dla wielu użytkowników i potrzebujesz prędkości, użyj 1st. jeśli masz aplikację dla krótkoterminowej aplikacji jednego użytkownika, może możesz użyć obu lub 2nd będzie bardziej "naturalny" dla deweloperzy.

Zdrówko.
 1
Author: Eduardo Mass,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-01 17:25:37

Dla tylko dwóch ciągów, zdecydowanie nie chcesz używać StringBuilder. Istnieje pewien próg, powyżej którego narzut StringBuilder jest mniejszy niż narzut przydzielania wielu ciągów.

Więc, aby uzyskać więcej niż 2-3 ciągów, użyj kodu Dannysmurfa . W przeciwnym razie wystarczy użyć operatora+.

 0
Author: Nick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:19

To zależy od kodu. StringBuilder jest ogólnie bardziej wydajny, ale jeśli łączysz tylko kilka ciągów i robisz to wszystko w jednej linii, optymalizacje kodu prawdopodobnie zajmą się tym za Ciebie. Ważne jest, aby pomyśleć o tym, jak wygląda kod: dla większych zestawów StringBuilder ułatwi czytanie, dla małych StringBuilder tylko doda niepotrzebnego bałaganu.

 0
Author: Jon Dewees,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-21 20:34:49

Inne rozwiązanie:

Wewnątrz pętli użyj List zamiast string.

List<string> lst= new List<string>();

for(int i=0; i<100000; i++){
    ...........
    lst.Add(...);
}
return String.Join("", lst.ToArray());;
Jest bardzo szybki.
 0
Author: asady,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-19 12:19:52