StringBuilder, инициализированный со строкой, содержат точно (только) достаточно пространства для той строки?

Я задаюсь вопросом если этот код...

StringBuilder sb = new StringBuilder("Please read the following messages.");

... инициализирует sb с буфером, точно столь большим, как строка передала конструктору. С одной стороны, это казалось бы самой логической вещью. С другой стороны, это кажется виду поражения целью StringBuilder класс для одного из его наиболее популярных способов использования, который должен обеспечить переменчивость для создания повторенным, добавляет более эффективный. (Самый первый вызов к Append, если бы ответ на мой вопрос - "да", потребовал бы sb изменить размер себя.)

С другой стороны я предполагаю, что можно было просмотреть это как аналогичное конструктору для List это берет IEnumerable в качестве параметра. Возможно, предположение в этом случае - то, что Вы не планирование добавления много, а скорее при управлении, что уже там.

Единственное реальное исследование, которое я провел на этом, должно было проверить документацию MSDN относительно StringBuilder, который не предоставлял ответ (это говорит, что конструктор инициализирует экземпляр "использование указанной строки", но не указывает, как строка используется).


Править: Таким образом, это является "определенным для реализации"... разве, это не кажется странным никому больше? Я имею в виду, цель StringBuilder класс должен предложить альтернативу выполнению большого количества операций на a string, создание тонны неизменных string экземпляры по пути; поэтому, это для эффективности. Я чувствую, что поведение этого конструктора должно быть указанным, так, чтобы разработчик мог сделать обоснованное решение, как использовать его независимо от платформы.

Я имею в виду, это реализовано Microsoft определенный путь; они, возможно, легко поместили это в документацию (вынуждающий другие реализации следовать примеру). Просто персональный источник замешательства...

6
задан Dan Tao 15 February 2010 в 14:19
поделиться

4 ответа

Это деталь реализации, о которой вам не нужно беспокоиться. Однако, используя отражатель .NET и просматривая перегрузку конструктора (string, int32, int32, int32) (которую вызывают другие конструкторы), мы видим, что он выбирает емкость, кратную из 16 (следующий по величине по сравнению с запрошенным размером)

Редактировать

Фактически, это 16 x 2 ^ n, при этом значение «n» выбрано как следующий по величине размер

5
ответ дан 9 December 2019 в 20:43
поделиться

Конструктор, с которым вы связались, вероятно, привязан к StringBuilder (String, Int32, Int32, Int32) :

public StringBuilder(
  string value,
  int startIndex,
  int length,
  int capacity
)

Итак, для строки он, вероятно, пройдет через: строка, 0, строка.Длина, строка.Длина. Или что-то подобное, имеющее смысл в контексте StringBuilder.

2
ответ дан 9 December 2019 в 20:43
поделиться

Конструктор, который в конечном итоге вызывается, выглядит так:

// "Please read the following messages.".Length = 35
public StringBuilder(string value, int startIndex, int length, int capacity)
public StringBuilder("Please read the following messages.", 0, 
        "Please read the following messages.".Length, 16)

(Это не то, что другие ответы не предоставляют, это просто от отражателя)
Если емкость меньше длины строки, это в данном случае:

while (capacity < length)
{
    capacity *= 2;
    if (capacity < 0)
    {
        capacity = length;
        break;
    }
}

В Mono конструктор StringBuilder (string val) выделяет емкость для int.MaxValue до тех пор, пока не произойдет добавление.

Настоящий ответ заключается в методе, который в конечном итоге вызывается изнутри CLR, где длина - это емкость:

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string FastAllocateString(int length);

Я не могу найти источник для этого в SSCLI , однако Mono версия (\ mono \ metadata \ object.c) делает это так:

mono_string_new_size (MonoDomain *domain, gint32 len)
{
    MonoString *s;
    MonoVTable *vtable;
    size_t size = (sizeof (MonoString) + ((len + 1) * 2));

...
}

Это размер в байтах объекта MonoString плюс длина, умноженная на 2.

1
ответ дан 9 December 2019 в 20:43
поделиться

Я использовал этот класс, чтобы приблизить площадь фигуры в одном из моих проектов. Он медленный, но при высоком разрешении он все равно может быть быстрее, чем подсчет пикселей (потому что стоимость подсчета пикселей растет квадратично с разрешением, но количество отрезков линии на периметре растет линейно.)

import static java.lang.Double.NaN;

import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.FlatteningPathIterator;
import java.awt.geom.Line2D;
import java.awt.geom.PathIterator;

public abstract class Areas {
    public static double approxArea(Area area, double flatness, int limit) {
        PathIterator i =
            new FlatteningPathIterator(area.getPathIterator(identity),
                                       flatness,
                                       limit);
        return approxArea(i);
    }

    public static double approxArea(Area area, double flatness) {
        PathIterator i = area.getPathIterator(identity, flatness);
        return approxArea(i);
    }

    public static double approxArea(PathIterator i) {
        double a = 0.0;
        double[] coords = new double[6];
        double startX = NaN, startY = NaN;
        Line2D segment = new Line2D.Double(NaN, NaN, NaN, NaN);
        while (! i.isDone()) {
            int segType = i.currentSegment(coords);
            double x = coords[0], y = coords[1];
            switch (segType) {
            case PathIterator.SEG_CLOSE:
                segment.setLine(segment.getX2(), segment.getY2(), startX, startY);
                a += hexArea(segment);
                startX = startY = NaN;
                segment.setLine(NaN, NaN, NaN, NaN);
                break;
            case PathIterator.SEG_LINETO:
                segment.setLine(segment.getX2(), segment.getY2(), x, y);
                a += hexArea(segment);
                break;
            case PathIterator.SEG_MOVETO:
                startX = x;
                startY = y;
                segment.setLine(NaN, NaN, x, y);
                break;
            default:
                throw new IllegalArgumentException("PathIterator contains curved segments");
            }
            i.next();
        }
        if (Double.isNaN(a)) {
            throw new IllegalArgumentException("PathIterator contains an open path");
        } else {
            return 0.5 * Math.abs(a);
        }
    }

    private static double hexArea(Line2D seg) {
        return seg.getX1() * seg.getY2() - seg.getX2() * seg.getY1();
    }

    private static final AffineTransform identity =
        AffineTransform.getQuadrantRotateInstance(0);
}
-121--2805541-

В «многоуровневой» архитектуре (очень распространенный сценарий) самый глубокий лежащий компонент отвечает за предусматривать политики по данному вопросу (может возвращать shared _ ptr < > , как предложено выше, или "вызывающий абонент несет ответственность за удаление этого" или "никогда не удалять это, но вызвать releaseFooObject() при выполнении и отсутствии доступа к нему после "или...), и компонент ближе к пользователю отвечает за соблюдение этой политики.

Двунаправленный информационный поток усложняет описание обязанностей.


ограничивается ли эта проблема только библиотеками DLL или же общие объекты UNIX тоже «причиняются»?

На самом деле это хуже, чем это: вы можете иметь эту проблему так же легко со статически связанными библиотеками. Именно существование границ кода внутри одного контекста выполнения создает возможность неправильного использования или неправильного сообщения о каком-либо объекте.

-121--2145729-

Проверьте члена емкости StringBuilder.

Из MSDN :

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

4
ответ дан 9 December 2019 в 20:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: