Содержание
Как известно, в C# строки относятся к неизменяемым типам данных, то есть, когда мы производим над переменной типа string
какие-либо операции, то в памяти создается новая строка, затем в новую строку копируются символы из старой строки и только потом старая строка удаляется из памяти. Таким образом, производить большое количество изменений строки становится слишком накладным, как в плане использования памяти, так и производительности приложения, в принципе. Однако, используя класс C# StringBuilder
мы можем избежать описанных выше проблем.
Класс StringBuilder
StringBuilder — это класс для работы со строками. Расположен этот класс в пространстве имен System.Text
. В отличие от String
, класс StringBuilder
представляет изменяемую последовательность символов и, следовательно, позволяющий более рационально использовать память.
У класса StringBuilder
определены следующие методы:
Append |
Добавляет строковое представление указанного значения к экземпляру StringBuilder . Используя этот метод, можно без явных преобразований добавлять к строке числа, массивы символов, логические значения, объекты и т.д. |
Append |
Добавляет к экземпляру StringBuilder строку, возвращаемую в результате форматирования исходной строки. |
Append |
Сцепляет строковые представления элементов из указанного массива объектов, помещая между ними заданный символьный разделитель, а затем добавляет результат к текущему экземпляру StringBuilder . Имеется ряд перегруженных версий этого метода, позволяющих предоставлять в качестве второго параметра ряд массивов других типов значений. |
Append |
Добавляет символ переноса строки по умолчанию в конец текущего объекта StringBuilder . |
Clear |
Удаляет все символы из текущего экземпляра StringBuilder . |
Copy |
Копирует символы из указанного сегмента этого экземпляра StringBuilder в указанный массив Char . |
Insert |
Вставляет строковое представление указанного типа в StringBuilder в указанную позицию. |
Remove |
Удаляет указанный диапазон символов из данного экземпляра. |
Replace |
Замещает все вхождения указанного символа в данном экземпляре на другой указанный знак. |
To |
Преобразует значение данного экземпляра в String . |
Рассмотрим пример использования StringBuilder
для работы со строками:
using System.Text; namespace SBuilder { internal class Program { static void Main(string[] args) { StringBuilder stringBuilder= new StringBuilder();//создали экземпляр stringBuilder.AppendLine("Hello world"); //добавили строку stringBuilder.AppendLine("Этот текст будет на второй строке"); //добавили вторую строку //далее будет одна строка stringBuilder.Append("Сегодня: "); stringBuilder.AppendFormat("{0} время {1}", DateOnly.FromDateTime(DateTime.Now), TimeOnly.FromDateTime(DateTime.Now)); Console.WriteLine(stringBuilder.ToString()); } } }
Результат вывода в консоли будет следующий:
Этот текст будет на второй строке
Сегодня: 19.11.2022 время 16:22
Здесь мы, используя StringBuilder
создали строку и вывели её в консоль. При этом, в примере явно отсутствует ответ на вопрос — в чем наглядное преимущество использования этого класса по сравнению с обычным string
? Чтобы ответить на этот вопрос, измерим время выполнения большого количества операций объединения строк, используя string и StringBuilder
.
Производительность StringBuilder
Так как на время выполнения той или иной операции оказывает влияние в том числе и окружение в котором происходит тестирование, то указанные ниже результаты можно считать ориентировочными.
Метод Append
Засекать время операции будем от начала создания объекта и до окончания операций объединения строк:
static void Main(string[] args) { int Stringlength = 10; //итоговая длина строки int repeatCount = 100;//количество повторений создания строки Stopwatch stopwatch = new(); long total = 0; for (int k = 0; k < repeatCount; k++) { stopwatch.Restart(); string str = ""; for (int i = 0; i < Stringlength; i++) { str += "0"; } stopwatch.Stop(); total += stopwatch.Elapsed.Ticks; } Console.WriteLine($"Среднее время {total/ repeatCount}"); total= 0; for (int k = 0; k < repeatCount; k++) { stopwatch.Restart(); StringBuilder builder = new StringBuilder(); for (int i = 0; i < Stringlength; i++) { builder.Append("0"); } stopwatch.Stop(); total += stopwatch.Elapsed.Ticks; } Console.WriteLine($"Среднее время {total / repeatCount}"); } } }
Результаты
Длина строки | string | StringBuilder |
10 | 4 | 2 |
100 | 86 | 11 |
1000 | 2420 | 51 |
10000 | 172250 | 697 |
100000 | 16507314 | 5861 |
Метод Insert
Здесь будем вставлять один символ в начало уже созданной ранее строки
Длина строки | string | StringBuilder |
10 | 5 | 5 |
100 | 87 | 39 |
1000 | 2426 | 1401 |
10000 | 175578 | 123306 |
100000 | 16321564 | 14974280 |
Как можно видеть из результатов, StringBuilder
значительно ускоряет работу с конкатенацией строк, хотя при выполнении операции вставки подстроки в строку выигрыш и не столь очевидный. Но, опять же оговорюсь — многое в тестах зависит и от окружения в котором эти тесты выполнялись и, возможно, что у вас результаты будут выглядеть несколько иначе.
Свойство Capacity
Длина строки в StringBuilder
, как и у string
хранится в свойстве Length
. При этом, у StringBuilder
имеется ещё одно полезное свойство — Capacity
, которое возвращает или задает максимальное число знаков, которое может содержаться в памяти. По умолчанию, при каждой операции в StringBuilder
происходит выделение памяти под строку, что несколько замедляет работу. Если же нам изначально известна конечная длина строки, которую мы должны получить, то создав экземпляр StringBuilder
с заранее заданным свойством Capacity
мы можем повысить производительность. Чтобы задать значение Capacity
при создании экземпляра StringBuilder
необходимо передать значение в конструктор:
Capacity = 1000; StringBuilder builder = new StringBuilder(Capacity);
Например, создание строки длиной 10 000 символов методом Append
без использования значения свойства Capacity
в конструкторе у меня заняло время равное 6440 тактам, а при использовании — 5677.
Когда использовать StringBuilder
В вопросе типа «string vs String Builder» следует придерживаться рекомендаций разработчиков из Microsoft, а именно StringBuilder
рекомендуется использовать в следующих случаях:
- Если предполагается, что код будет вносить неизвестное количество изменений в строку во время разработки (например, при использовании цикла для сцепления случайного числа строк, содержащих данные, введенные пользователем).
- Если предполагается, что код вносит значительное количество изменений в строку.
Также, можно отметить, что StringBuilder
не содержит методов поиска подстрок в строке, поэтому, при выборе между string
и StringBuilder
стоит обратить внимание и на то, предполагается ли производить в строке большое количество операций поиска. Если да, то, возможно, стоит отказаться от StringBuilder
или же производить необходимые операции поиска перед добавлением строки в StringBuilder
.
Итого
Класс StringBuilder
предоставляет методы для работы со строками в C#. При этом, StringBuilder
может значительно повысить производительность приложения, если предполагается использование большого количества операций объединения строк. Наличие большого количества перегруженных методов у StringBuilder
позволяет без дополнительных преобразований производить конкатенацию строк, используя различные типы данных.