среда, 20 августа 2014 г.

Склеиваем StringBuilder`ы

  Давно уже как исследовал один интересный вопрос, но все никак не мог добраться до блога, чтобы написать. Но не было счастья, так несчастье помогло.
  Исходные данные в свое время были такие: есть приложение, которое активно работает со строками. Читает их из файлов, баз данных, анализирует, конкатенирует и т.д. Так получилось, что автору нравилось очень работать с StringBuilder. Они были везде, в том числе возвращались из каждого второго метода.

  Я заметил интересную вещь, автор при добавлении одного StringBuilder к другому вызывал ToString (имеется ввиду AppendLine). Получается интересная ситуация. Вроде как StringBuilder призван, чтобы оптимизировать работу со строками. Но с другой стороны конкатенация самих StringBuilder принуждает кратковременно отказаться от его оптимизаций.
  Действительно, AppendLine принимает только string. Но если посмотреть на Append, то там гора методов для самых разнообразных параметров. Почти все они базовые типы.
  Есть метод Append, которые принимает параметр типа Object - это хорошо. Это вселяет надежду, что можно передать StringBuilder. Может внутри будет угадан настоящий тип параметра и использован какой-нибудь internal/private метод? Смотрим реализацию отсюда

public StringBuilder Append(Object value) {
            Contract.Ensures(Contract.Result<StringBuilder>() != null);
 
            if (null==value) {
                //Appending null is now a no-op.
                return this;
            }
            return Append(value.ToString());
        }

  Чуда не случилось, StringBuilder опять вызывает ToString().
  Хорошо, покопаемся в самом StringBuilder. Может тут действительно нет места оптимизации.
  StringBuilder представляет из себя односвязный список массивов символов.

internal char[] m_ChunkChars;// The characters in this block
internal StringBuilder m_ChunkPrevious;// Link to the block logically before this block
internal int m_ChunkLength;// The index in m_ChunkChars that represent the end of the block
internal int m_ChunkOffset;// The logial offset (sum of all characters in previous blocks)

  То есть каждый экземпляр StringBuilder выделяет текст блоками. Если блок заканчивается, то в лучшем случае выделяется новый блок, а на старый оставляется ссылка. Конкатенация StringBuilder ложится на такую структуру идеально.
  Посмотрим, как StringBuilder выглядит в Java:


public StringBuilder append(CharSequence s) 
{
        if (s == null)
            s = "null";
        if (s instanceof String)
            return this.append((String)s);
        if (s instanceof StringBuffer)
            return this.append((StringBuffer)s);
        if (s instanceof StringBuilder)
            return this.append((StringBuilder)s);
        return this.append(s, 0, s.length());
}

  То есть тип проверяется и вызывается метод, оптимизированный для конкатенации StringBuilder.
  Выходит, что в .NET лучше не пользоваться несколькими StringBuilder, если нужно склеивать один текст. Обратное грозит как минимум дополнительными выделениями памяти и фрагментацией.

Комментариев нет:

Отправить комментарий