Подтвердить что ты не робот

Когда использовать StringBuilder?

Я понимаю преимущества StringBuilder.

Но если я хочу конкатенировать 2 строки, то я предполагаю, что лучше (быстрее) сделать это без StringBuilder. Правильно ли это?

В какой момент (количество строк) становится лучше использовать StringBuilder?

4b9b3361

Ответ 1

Я тепло предлагаю вам прочитать The Sad Tragedy of the Micro-Optimization Theatre от Джеффа Этвуда.

Он обрабатывает Simple Concatenation vs. StringBuilder и другие методы.

Теперь, если вы хотите увидеть некоторые цифры и графики, перейдите по ссылке;)

Ответ 2

  Но если я хочу объединить 2 строки, тогда я предполагаю, что это лучше (быстрее) сделать это без StringBuilder. Это правильно?

Это действительно правильно, вы можете найти, почему точно объяснил очень хорошо:

http://www.yoda.arachsys.com/csharp/stringbuilder.html

Подводя итог: если вы можете объединить строки за один раз, как

var result = a + " " + b  + " " + c + ..

вам лучше без StringBuilder, потому что создается только копия (длина результирующей строки вычисляется заранее.);

Для структуры типа

var result = a;
result  += " ";
result  += b;
result  += " ";
result  += c;
..

новые объекты создаются каждый раз, поэтому вам следует рассмотреть StringBuilder.

В конце статьи приводятся общие правила:

Правила большого пальца

Итак, когда вы должны использовать StringBuilder, и когда вы должны использовать строку операторы конкатенации?

  • Определенно используйте StringBuilder, когда вы объединяете в нетривиальном петля - особенно если вы не знаете наверняка (во время компиляции) сколько итерации, которые вы будете делать через петля. Например, чтение файла характер за один раз, создавая Строка, как вы идете, используя оператор + = потенциально самоубийство производительности.

  • Обязательно используйте конкатенацию оператор, когда вы можете (читаемо) указать все, что должно быть объединены в одном заявлении. (Если ты иметь множество вещей, чтобы объединить, рассмотреть вопрос о звонке String.Concat явно - или String.Join, если вам нужен разделитель.)

  • Не бойтесь разбить литералы на несколько сцепленных битов - результат будет таким же. Вы можете помочь удобочитаемость, ломая длинный литерал в несколько строк, например, с без ущерба для производительности.

  • Если вам нужны промежуточные результаты конкатенации чего-то кроме подачи следующей итерации конкатенации, StringBuilder не является собираюсь помочь тебе. Например, если вы создаете полное имя из первого имя и фамилию, а затем добавить третья часть информации ( прозвище, может быть) до конца только выгода от использования StringBuilder если вам не нужно (имя + фамилия) строка для других целей (как мы делаем в примере, который создает объект Person).

  • Если у вас есть несколько объединений делать, и вы действительно хотите сделать их в отдельных заявлениях, это не действительно важно, каким путем вы идете. Который Более эффективный способ будет зависеть от количество конкатенаций размеров строки и в каком порядке они соединены. Если вы действительно считаю, что кусок кода, чтобы быть узкое место, профиль или сравните это в обоих направлениях.

Ответ 3

System.String - неизменяемый объект - это означает, что всякий раз, когда вы изменяете его содержимое, он выделяет новую строку, и для этого требуется время (и память?). Используя StringBuilder, вы изменяете фактическое содержимое объекта без выделения нового.

Поэтому используйте StringBuilder, когда вам нужно сделать много изменений в строке.

Ответ 4

Не совсем... вы должны использовать StringBuilder, если вы объединяете большие строки или у вас много конкатенаций, например, в цикле.

Ответ 5

  • Если вы объединяете строки в цикле, вам следует использовать StringBuilder вместо обычной строки
  • В случае единственной конкатенации вы можете не видеть разницу во времени выполнения на всех

Вот простое тестовое приложение, чтобы доказать точку:

class Program
{
    static void Main(string[] args)
    {
        const int testLength = 30000;
        var StartTime = DateTime.Now;

        //TEST 1 - String
        StartTime = DateTime.Now;
        String tString = "test string";
        for (int i = 0; i < testLength; i++)
        {
            tString += i.ToString();
        }
        Console.WriteLine((DateTime.Now - StartTime).TotalMilliseconds.ToString());
        //result: 2000 ms

        //TEST 2 - StringBuilder
        StartTime = DateTime.Now;
        StringBuilder tSB = new StringBuilder("test string");
        for (int i = 0; i < testLength; i++)
        {
            tSB.Append(i.ToString());
        }
        Console.WriteLine((DateTime.Now - StartTime).TotalMilliseconds.ToString());
        //result: 4 ms

        Console.ReadLine();
    }
}

Результаты:

  • 30'000 итераций

    • String - 2000 мс
    • StringBuilder - 4 мс
  • 1000 итераций

    • String - 2 мс
    • StringBuilder - 1 мс
  • 500 итераций

    • String - 0 мс
    • StringBuilder - 0 мс

Ответ 6

  Но если я хочу объединить 2 строки, то я предполагаю, что лучше и быстрее сделать это без StringBuilder. Это правильно?

Да. Но что более важно, в таких ситуациях гораздо удобнее использовать ваниль String. С другой стороны, использование его в цикле имеет смысл, а также может быть таким же удобным для чтения, как и конкатенация.

Я бы с осторожностью следил за эмпирическими правилами, в которых в качестве порогового значения приводятся конкретные числа конкатенации. Использование его в циклах (и только в циклах), вероятно, столь же полезно, легче запомнить и имеет больше смысла.

Ответ 7

Нет окончательного ответа, только правила большого пальца. Мои личные правила выходят примерно так:

  • Если конкатенировать в цикле, всегда используйте StringBuilder.
  • Если строки большие, всегда используйте StringBuilder.
  • Если код конкатенации аккуратный и читаемый на экране, то, вероятно, это нормально.
    Если это не так, используйте StringBuilder.

Ответ 8

Перефразировать

Тогда будешь считать до трех, не больше, не меньше. Три - это число, которое вы должны подсчитать, а число подсчетов должно быть три. Четыре не засчитываешь, и не считай двух, кроме того, что ты переходишь к трем. Как только число три, будучи третьим номером, достигнем, тогда проглотите свою Святую Ручную гранату Антиохии

Я обычно использую строковый построитель для любого блока кода, который приведет к конкатенации трех или более строк.

Ответ 9

Пока вы можете физически вводить количество конкатенаций (a + b + c...), это не должно иметь большого значения. Квадрат N (при N = 10) представляет собой 100-кратное замедление, которое не должно быть слишком плохим.

Большая проблема заключается в том, что вы объединяете сотни строк. При N = 100 вы получаете замедление в 10000 раз. Это довольно плохо.

Ответ 10

Я не думаю, что существует тонкая грань между тем, когда использовать или когда этого не делать. Если, конечно, кто-то не выполнил некоторые обширные испытания, чтобы выйти с золотыми условиями.

Для меня я не буду использовать StringBuilder, если просто конкатенировать 2 огромные строки. Если цикл с неопределенным подсчетом, я, скорее всего, даже если цикл может быть небольшим.

Ответ 11

Одиночная конкатенация не стоит использовать StringBuilder. Я, как правило, использовал 5 конкатенаций.

Ответ 12

Поскольку было трудно найти объяснение этому, на которое бы не влияли мнения или не последовало сражение гордости, я решил написать на LINQpad немного кода, чтобы проверить это сам.

Я обнаружил, что использование строк небольшого размера вместо использования i.ToString() изменяет время отклика (отображается в маленьких циклах).

В тесте используются разные последовательности итераций для измерения времени в разумно сопоставимых диапазонах.

В конце я скопирую код, чтобы вы могли попробовать его самостоятельно (results.Charts... Dump() не будет работать вне LINQPad).

Выходные данные (ось Y: количество проверенных итераций, ось X: отдельные тесты):

Последовательность итераций: 2, 3, 4, 5, 6, 7, 8, 9, 10 Iterations sequence: 2, 3, 4, 5, 6, 7, 8, 9, 10

Последовательность итераций: 10, 20, 30, 40, 50, 60, 70, 80 Iterations sequence: 10, 20, 30, 40, 50, 60, 70, 80

Последовательность итераций: 100, 200, 300, 400, 500 Iterations sequence: 100, 200, 300, 400, 500

Код (написанный с использованием LINQPad 5):

void Main()
{
    Test(2, 3, 4, 5, 6, 7, 8, 9, 10);
    Test(10, 20, 30, 40, 50, 60, 70, 80);
    Test(100, 200, 300, 400, 500);
}

void Test(params int[] iterationsCounts)
{
    $"Iterations sequence: {string.Join(", ", iterationsCounts)}".Dump();

    int testStringLength = 10;
    RandomStringGenerator.Setup(testStringLength);
    var sw = new System.Diagnostics.Stopwatch();
    var results = new Dictionary<int, TimeSpan[]>();

    // This call before starting to measure time removes initial overhead from first measurement
    RandomStringGenerator.GetRandomString(); 

    foreach (var iterationsCount in iterationsCounts)
    {
        TimeSpan elapsedForString, elapsedForSb;

        // string
        sw.Restart();
        var str = string.Empty;

        for (int i = 0; i < iterationsCount; i++)
        {
            str += RandomStringGenerator.GetRandomString();
        }

        sw.Stop();
        elapsedForString = sw.Elapsed;


        // string builder
        sw.Restart();
        var sb = new StringBuilder(string.Empty);

        for (int i = 0; i < iterationsCount; i++)
        {
            sb.Append(RandomStringGenerator.GetRandomString());
        }

        sw.Stop();
        elapsedForSb = sw.Elapsed;

        results.Add(iterationsCount, new TimeSpan[] { elapsedForString, elapsedForSb });
    }


    // Results
    results.Chart(r => r.Key)
    .AddYSeries(r => r.Value[0].Ticks, LINQPad.Util.SeriesType.Line, "String")
    .AddYSeries(r => r.Value[1].Ticks, LINQPad.Util.SeriesType.Line, "String Builder")
    .DumpInline();
}

static class RandomStringGenerator
{
    static Random r;
    static string[] strings;

    public static void Setup(int testStringLength)
    {
        r = new Random(DateTime.Now.Millisecond);

        strings = new string[10];
        for (int i = 0; i < strings.Length; i++)
        {
            strings[i] = Guid.NewGuid().ToString().Substring(0, testStringLength);
        }
    }

    public static string GetRandomString()
    {
        var indx = r.Next(0, strings.Length);
        return strings[indx];
    }
}