stringbuilder c что это
Класс StringBuilder
Когда строка конструируется классом String, выделяется ровно столько памяти, сколько необходимо для хранения данной строки. Однако, в пространстве имен System.Text имеется класс StringBuilder, который поступает лучше и обычно выделяет больше памяти, чем нужно в данный момент. У вас, как разработчика, есть возможность указать, сколько именно памяти должен выделить StringBuilder, но если вы этого не сделаете, то будет выбран объем по умолчанию, который зависит от размера начального текста, инициализирующего экземпляр StringBuilder. Класс StringBuilder имеет два главных свойства:
Length, показывающее длину строки, содержащуюся в объекте в данный момент
Capacity, указывающее максимальную длину строки, которая может поместиться в выделенную для объекта память
Любые модификации строки происходят внутри блока памяти, выделенного экземпляру StringBuilder. Это делает добавление подстрок и замену индивидуальных символов строки очень эффективными. Удаление или вставка подстрок неизбежно остаются менее эффективными, потому что при этих операциях приходится перемещать в памяти части строки. Выделять новую память и, возможно, полностью перемещать ее содержимое приходится только при выполнении ряда действий, которые приводят к превышению выделенной емкости строки. В дополнение к избыточной памяти, выделяемой изначально на основе экспериментов, StringBuilder имеет свойство удваивать свою емкость, когда происходит переполнение, а новое значение емкости не установлено явно.
В данном примере начальная емкость StringBuilder равна 120. Всегда лучше сразу указывать емкость, превышающую предполагаемую длину строки, чтобы объекту StringBuilder не приходилось заново выделять память при переполнении. По умолчанию устанавливается емкость в 16 символов. Схематически данный пример работает следующим образом:
Т.е. при вызове метода AppendFormat() остаток текста помещается в пустое пространство без необходимости перераспределения памяти. Однако реальная эффективность, которую несет с собой применение StringBuilder, проявляется при выполнении повторных подстановок текста.
В следующей таблице перечислены основные методы класса StringBuilder:
Метод | Назначение |
---|---|
Append() | Добавляет строку к текущей строке |
AppendFormat() | Добавляет строку, сформированную в соответствии со спецификатором формата |
Insert() | Вставляет подстроку в строку |
Remove() | Удаляет символ из текущей строки |
Replace() | Заменяет все вхождения символа другим символом или вхождения подстроки другой подстрокой |
ToString() | Возвращает текущую строку в виде объекта System.String (переопределение метода класса System.Object) |
Давайте рассмотрим практический пример использования класса StringBuilder:
Объект String является неизменяемым. Каждый раз при использовании одного из методов в классе System.String вы создаете объект строки в памяти, для которого требуется выделение нового пространства. В случаях, когда необходимо выполнять повторяющиеся изменения строки, издержки, связанные с созданием объекта String, могут оказаться значительными. Чтобы изменять строку без создания нового объекта, можно использовать класс System.Text.StringBuilder. Например, использование класса StringBuilder может повысить производительность при соединении большого количества строк в цикле.
Импорт пространства имен System.Text
Класс StringBuilder находится в пространстве имен System.Text. Чтобы не указывать в коде полное имя типа, вы можете импортировать пространство имен System.Text:
Создание экземпляров объекта StringBuilder
Вы можете создать экземпляр класса StringBuilder путем инициализации переменной с помощью одного из перегруженных методов конструктора, как показано в следующем примере.
Настройка емкости и длины
Хотя StringBuilder является динамическим объектом, который позволяет вам развернуть ряд символов в строке, которые она инкапсулирует, вы можете указать максимальное число содержащихся в ней символов. Это значение называется емкостью объекта, и его не следует путать с длиной строки, которую содержит текущий объект StringBuilder. Например, вы можете создать экземпляр класса StringBuilder со строкой Hello, длина которой составляет 5 символов, указав при этом максимальную емкость объекта 25 символов. При изменении значения StringBuilder размер не перераспределяется, если не достигнута максимальная емкость. Когда это происходит, новое пространство выделяется автоматически, а емкость удваивается. Вы можете указать емкость класса StringBuilder с помощью одного из перегруженных конструкторов. В следующем примере показано, что объект myStringBuilder можно развернуть максимум на 25 позиций.
Кроме того, вы можете использовать доступное для чтения или записи свойство Capacity, чтобы задать максимальную длину объекта. В следующем примере используется свойство Capacity для определения максимальной длины объекта.
Метод EnsureCapacity можно использовать для проверки емкости текущего объекта StringBuilder. Если емкость больше переданного значения, изменения не вносятся; однако если емкость меньше переданного значения, текущая емкость изменяется для соответствия переданному значению.
Можно также просмотреть или задать свойство Length. Если вы задали для свойства Length значение больше значения свойства Capacity, значение свойства Capacity будет автоматически изменено на то же самое значение, что и у свойства Length. Если задать для свойства Length значение меньше длины строки в текущем объекте StringBuilder, строка будет укорочена.
Изменение строки StringBuilder
В следующей таблице перечислены методы, которые можно использовать для изменения содержимого объекта StringBuilder.
Имя метода | Использовать |
---|---|
StringBuilder.Append | Добавляет сведения в конец текущего объекта StringBuilder. |
StringBuilder.AppendFormat | Заменяет отформатированным текстом описатель формата, переданный в строке. |
StringBuilder.Insert | Вставляет строку или объект в указанный индекс текущего объекта StringBuilder. |
StringBuilder.Remove | Удаляет указанное количество символов из текущего объекта StringBuilder. |
StringBuilder.Replace | Замещает все вхождения указанного символа или строки в текущем StringBuilder на другой указанный символ или строку. |
Добавить
Метод Append позволяет добавить текст или строковое представление объекта к концу строки, представленной текущим объектом StringBuilder. Следующий пример инициализирует StringBuilder с текстом Hello World, а затем добавляет текст в конец объекта. Пространство выделяется автоматически при необходимости.
AppendFormat
Метод StringBuilder.AppendFormat добавляет текс в конец объекта StringBuilder. Он поддерживает возможность составного форматирования (дополнительные сведения см. в статье Составное форматирование) путем вызова реализации IFormattable для одного или нескольких форматируемых объектов. Следовательно, он принимает строки стандартного формата для числовых значений, значений даты и времени, значений перечисления, строки пользовательского формата для чисел и дат и строки формата, определенные для пользовательских типов. (Дополнительные сведения о форматировании см. в разделе Типы форматирования.) Вы можете использовать этот метод для настройки формата переменных и добавления этих значений к StringBuilder. В следующем примере метод AppendFormat помещает в конец объекта StringBuilder целочисленное значение, отформатированное в виде значения валюты.
Insert
Метод Insert добавляет строку или объект в указанную позицию в текущем объекте StringBuilder. В следующем примере этот метод вставляет слово в шестую позицию объекта StringBuilder.
Удалить
Вы можете использовать метод Remove, чтобы удалить указанное количество символов из текущего объекта StringBuilder, начиная с указанного индекса (с отсчетом с нуля). В следующем примере метод Remove используется для сокращения объекта StringBuilder.
Замените
Метод Replace можно использовать для замены символов в объекте StringBuilder другими указанными символами. В следующем примере метод Replace находит все экземпляры восклицательного знака (!) в объекте StringBuilder и заменяет их знаками вопроса (?).
BestProg
Содержание
Поиск на других ресурсах:
Отличие между классами String и StringBuilder состоит в следующем.
память для нового строчного литерала ( «Text2» ) перераспределяется по новому: выделяется новый участок памяти, в который копируется новая измененная строка. Память, выделенная под предыдущий фрагмент, в дальнейшем уничтожается сборщиком мусора (Garbage collector).
В классе StringBuilder при изменении строки-оригинала, память для новой строки выделяется только в случае, когда размер новой строки больше размера строки-оригинала. При этом, памяти выделяется в два раза больше предыдущего размера. Если размер новой строки меньше или равен размеру строки-оригинала, то память не перераспределяется. Такой механизм позволяет повысить быстродействие за счет уменьшения интенсивности (количества) выделения новых ресурсов и освобождения использованных ресурсов.
Чтобы использовать сокращенное имя класса StringBuilder нужно предварительно подключить пространство имен System.Text
3. Операция присваивания = для класса StringBuilder
В классе System.Text.StringBuilder оператор присваивания не определен. Поэтому, при объявлении объекта класса StringBuilder
использовать оператор присваивания = для присваивания объекту значения
не удастся. В этом случае компилятор выдаст ошибку
4. Операция присваивания одиночного символа по индексу в классах String и StringBuilder
Между классами System.String и System.Text.StringBuilder существует отличие при использовании операции присваивания по индексу одиночного символа.
Переменной (объекту) типа String нельзя присваивать одиночный символ по индексу как показано ниже
Одиночный символ можно только прочитать
Класс StringBuilder имеет несколько конструкторов, которые выделяют память а также инициализируют экземпляры этого класса. Ниже приведены примеры создания экземпляров класса (объектов) StringBuilder
6. Методы класса StringBuilder
Метод Append() предназначен для добавления копии экземпляра некоторого типа к текущему экземпляру. Метод имеет много перегруженных реализаций для разных типов.
6.2. Изменение отдельного символа в строке. Доступ по индексу
Метод Insert() предназначен для вставки объектов разных типов в заданную позицию строки. Метод имеет много перегруженных реализаций. Во всех реализациях первый параметр метода задает позицию вставки.
Пример.
Метод Remove() используется для удаления из заданной строки диапазона символов начиная с заданной позиции. Метод получает два целочисленных параметра. Первый параметр указывает индекс начала, из которого удаляется строка. Второй параметр указывает количество удаляемых символов.
Пример.
Метод Replace() используется для замены подстроки в строке. Метод работает как для одиночных символов так и для строк. Метод имеет несколько перегруженных реализаций, среди которых выделяются четыре нижеследующие:
Пример.
Метод Clear() реализует очистку строки (удаление всех символов в строке).
Пример.
Метод CopyTo() копирует символы из исходной строки в массив типа char ( System.Char ). Метод получает 4 параметра, которые имеют следующее назначение:
Пример.
Пример.
В классе StringBuilder реализованы три целочисленные свойства, которые определяют характеристики строки:
StringBuilder прошлое и настоящее
Вступление
Что же плохого в этом коде? А то, что на каждой итерации создается строка длиною на единицу больше чем на предыдущем шаге с последующим копированием символов из старой строки. Таким образом, суммарное количество задействованных символов равно:
Данная формула есть не что иное, как сумма арифметической прогрессии:
То есть такой сценарий конкатенации требует памяти пропорционально O(n 2 ), n — длина строки.
Для устранения проблем в подобном коде мы используем класс StringBuilder, зная, что операции с ним не приводят к таким растратам памяти как со String. Фактически StringBuilder представляет собой изменяемую строку.
Такой код хоть и не лишен всех недостатков и тратит некоторую память на ветер, но делает это более сдержанно, по сравнению с предыдущим кодом.
m_currentThread — идентификатор потока, в котором создавался экземпляр объекта;
m_MaxCapacity — максимальная емкость данного экземпляра;
m_StringValue — строка, содержащая символы.
Фактически класс StringBuilder внутри работает со строковым типом данных String. Поскольку строки со стороны mscorlib.dll являются изменяемыми, то StringBuilder-у ничего не стоит изменить строку, находящуюся в m_StringValue.
Первоначальная длина составляет 16 символов, а при нехватке места для добавления новых символов StringBuilder заменяет внутреннюю строку на строку длиною в два раза больше и копирует во вновь созданную все символы из предыдущей + новые. Удвоение длины строки приводит к линейной сложности (O(n)) по памяти, в отличие от квадратичной, которая присуща обычным строкам.
Важным дополнением к увеличению производительности является задание нужной емкости при создании экземпляра класса StringBuilder. Тем самым нам не требуется очередное удвоение размера и копирование данных при нехватке памяти. Однако, это возможно лишь в том случае, если мы заранее знаем размер получаемой строки.
Рассмотрим реализации наиболее часто используемых методов. Для читаемости кода я убрал условия проверки входных параметров.
Метод Append()
Данный метод просто проверяет, хватает ли места в текущем экземпляре для добавления новой строки, если да, то просто происходит копирование на месте в незанятую часть строки, иначе удвоение размера и копирование старой и новой строки.
Метод Insert()
Данный метод аналогично предыдущему проверяет, хватает ли места в текущем экземпляре для вставки новой строки и в зависимости от этого удваивает размер строки или же вставляет на месте в исходную строку без изменения размера.
Метод Remove()
Данный метод удаляет ненужные символы, сдвигая оставшуюся часть строки влево. При удалении последнего символа фактически ничего сдвигать не надо, поэтому удаление с конца происходит намного быстрее, чем из любой другой части строки.
Метод ToString()
Причина такого изменения достаточно очевидна: при такой реализации не требуется перевыделять память при ее нехватке, что присуще предыдущей реализации. Это так же означает, что метод ToString() работает немного медленнее, поскольку окончательную строку необходимо сначала сформировать, а метод Append() работает быстрее, поскольку не требует копирования. Однако это вписывается в типичный сценарий использования для StringBuilder: много вызовов Append(), а затем один вызов ToString().
m_ChunkChars — массив содержащий символы текущего элемента связного списка (кусочка строки);
m_ChunkPrevious — ссылка на предыдущий элемент (StringBuilder) в списке;
m_ChunkLength — реальная длина текущего элемента списка (количество используемых символов);
m_ChunkOffset — суммарное количество используемых строкой символов (логическая длина);
m_MaxCapacity — максимальная емкость текущего экземпляра StringBuilder.
Максимальная длина элемента списка MaxChunkSize равна 8000. Как вы понимаете это сделано не просто так. Вот комментарий разработчиков класса:
We want to keep chunk arrays out of large object heap ( Исходный код
Метод Append() работает следующим образом: если в текущем элементе списка хватает символов для вставки новой строки, то происходит копирование в нее, если же нет, то копируется та часть которая помещается, а для того, что не поместилось, создается новый элемент списка (экземпляр StringBuilder-a), у которого длина массива равна длине всей исходной строки либо длине оставшейся строки в зависимости от того что больше. Однако, как было сказано, выше максимальная длина массива составляет 8000.
В общем, формула для вычисления длины нового элемента списка выглядит так:
где minBlockCharCount — оставшаяся длина строки после копирования ее части которая помещается в текущий экземпляр.
Таким образом, в результате работы такого кода
длины массивов у элементов списка будут равны: 8000, 4092, 2048, 1024, 512, 256, 128, 64, 32, 16, 16.
При таких длинах массивов операция обращения к определенному символу в исходной строке выполняется достаточно быстро практически за O(1), так как элементов списка не так много.
Метод Insert()
Метод Insert() работает следующим образом: если в текущем элементе списка(StringBuilder-е) хватает места для вставки, то имеющиеся символы сдвигаются, чтобы дать место новому тексту. Иначе же создается новый элемент списка (StringBuilder), в который копируется часть символов из предыдущего элемента, которые не поместились. Последующие символы не смещаются влево.
Что будет в результате такого кода?
Результат будет отличаться от кода использующего Append(), причем весьма серьезно!
Мы получим очень большой список StringBuilder-ов каждый элемент, которого будет иметь длину 16 символов. В результате чего операция обращения к определенному символу по индексу будет выполняться медленней, чем ожидалось, а именно пропорционально длине списка, то есть O(n).
Метод Remove()
Реализация данного метода существенно усложнилась. Однако надо учесть, что предыдущая реализация копировала большое количество символов, смещая их влево. Здесь же необходимо производить смещение только в пределах одного элемента (StringBuilder-а) в списке.
Метод ToString()
Данный метод проходит по всему связному списку StringBuilder-ов и последовательно копирует символы каждого из элементов списка в результирующую строку.
Сравнение производительности
Пожалуй, самая интересная часть — это сравнение производительности между двумя версиями класса.
Тест 1. Сколько требуется памяти для хранения строки заданной длины.
Как видите, при небольшой длине строки новая реализация проигрывает старой. Оно и понятно, ведь для каждого элемента списка(StringBuilder) требуется информация о длине, емкости, смещении от начала строки + для массива символов overhead. Но как только длина строки становится больше 16384, старая реализация начинает проигрывать (из-за удвоения размера строки, она содержит много неиспользуемых символов).
Тест 2. Метод Append()
Пожалуй, это тот самый метод, в котором новая реализация побеждает. Поскольку при нехватке памяти теперь удваивать длину строки и копировать в нее символы не требуется, то данный метод выполняется значительно быстрее, почти в два раза (точнее в 1,8 раз).
Тест 3. Метод Insert()
Будем производить вставку в строку уже заполненную символами, длиною 1000 символов.
1. Вставка в начало строки
2. Вставка в середину строки
3. Вставка в конец строки
Комментарии излишне — новая реализация проигрывает при вставке в любое место.
Тест 4. Метод Remove()
Будем производить удаление 10 символов из строки уже заполненной символами до тех пор, пока не исчерпаем ее.
Новая реализация выигрывает при удалении почти из любого места, так как теперь не требуется смещать символы оставшейся строки влево (точнее требуется, но не так часто и много как раньше).
Тест 5. Метод ToString()
Как было сказано выше, данный метод проигрывает предыдущей реализации. Предыдущая реализация возвращала просто ссылку на строку, которой она оперировала (при первом вызове), а новая вынуждена собирать результирующую строку по кускам, обходя каждый элемент связного списка.
Новая реализация работает заметно медленнее, если строка формировалась с помощью метода Insert(), поскольку список будет состоять из множества элементов(StringBuilder-ов) длиною в 16 символов.
Тест 6. Обращение по определенному индексу
Учитывая, что теперь StringBuilder представляет собой связный список, операция обращения к строке по определенному индексу становится дорогостоящей. Особенно если он формировался с помощью метода Insert.
Тест 7. Обычный сценарий: множество вызовов Append(), а затем вызов ToString()
Как правило, мы работаем с данным классом по определенному сценарию: множественный вызов метода Append(), за которым следует один вызов ToString(). Реализации данного класса поменялась именно в расчете на данный сценарий.
Вывод
Учитывая новую реализацию класса, при которой лишь множественный вызов метода Append() приводит к увеличению производительности, класс теперь можно было бы назвать StringAppender *)
The String object is immutable. Every time you use one of the methods in the System.String class, you create a new string object in memory, which requires a new allocation of space for that new object. In situations where you need to perform repeated modifications to a string, the overhead associated with creating a new String object can be costly. The System.Text.StringBuilder class can be used when you want to modify a string without creating a new object. For example, using the StringBuilder class can boost performance when concatenating many strings together in a loop.
Importing the System.Text Namespace
The StringBuilder class is found in the System.Text namespace. To avoid having to provide a fully qualified type name in your code, you can import the System.Text namespace:
Instantiating a StringBuilder Object
You can create a new instance of the StringBuilder class by initializing your variable with one of the overloaded constructor methods, as illustrated in the following example.
Setting the Capacity and Length
Although the StringBuilder is a dynamic object that allows you to expand the number of characters in the string that it encapsulates, you can specify a value for the maximum number of characters that it can hold. This value is called the capacity of the object and should not be confused with the length of the string that the current StringBuilder holds. For example, you might create a new instance of the StringBuilder class with the string «Hello», which has a length of 5, and you might specify that the object has a maximum capacity of 25. When you modify the StringBuilder, it does not reallocate size for itself until the capacity is reached. When this occurs, the new space is allocated automatically and the capacity is doubled. You can specify the capacity of the StringBuilder class using one of the overloaded constructors. The following example specifies that the myStringBuilder object can be expanded to a maximum of 25 spaces.
Additionally, you can use the read/write Capacity property to set the maximum length of your object. The following example uses the Capacity property to define the maximum object length.
The EnsureCapacity method can be used to check the capacity of the current StringBuilder. If the capacity is greater than the passed value, no change is made; however, if the capacity is smaller than the passed value, the current capacity is changed to match the passed value.
The Length property can also be viewed or set. If you set the Length property to a value that is greater than the Capacity property, the Capacity property is automatically changed to the same value as the Length property. Setting the Length property to a value that is less than the length of the string within the current StringBuilder shortens the string.
Modifying the StringBuilder String
The following table lists the methods you can use to modify the contents of a StringBuilder.
Method name | Use |
---|---|
StringBuilder.Append | Appends information to the end of the current StringBuilder. |
StringBuilder.AppendFormat | Replaces a format specifier passed in a string with formatted text. |
StringBuilder.Insert | Inserts a string or object into the specified index of the current StringBuilder. |
StringBuilder.Remove | Removes a specified number of characters from the current StringBuilder. |
StringBuilder.Replace | Replaces all occurrences of a specified character or string in the current StringBuilder with another specified character or string. |
Append
The Append method can be used to add text or a string representation of an object to the end of a string represented by the current StringBuilder. The following example initializes a StringBuilder to «Hello World» and then appends some text to the end of the object. Space is allocated automatically as needed.
AppendFormat
The StringBuilder.AppendFormat method adds text to the end of the StringBuilder object. It supports the composite formatting feature (for more information, see Composite Formatting) by calling the IFormattable implementation of the object or objects to be formatted. Therefore, it accepts the standard format strings for numeric, date and time, and enumeration values, the custom format strings for numeric and date and time values, and the format strings defined for custom types. (For information about formatting, see Formatting Types.) You can use this method to customize the format of variables and append those values to a StringBuilder. The following example uses the AppendFormat method to place an integer value formatted as a currency value at the end of a StringBuilder object.
Insert
The Insert method adds a string or object to a specified position in the current StringBuilder object. The following example uses this method to insert a word into the sixth position of a StringBuilder object.
Remove
You can use the Remove method to remove a specified number of characters from the current StringBuilder object, beginning at a specified zero-based index. The following example uses the Remove method to shorten a StringBuilder object.
Replace
The Replace method can be used to replace characters within the StringBuilder object with another specified character. The following example uses the Replace method to search a StringBuilder object for all instances of the exclamation point character (!) and replace them with the question mark character (?).