StringBuilder правильный размер для составных структур, таких как карта - PullRequest
0 голосов
/ 04 февраля 2012

В качестве примера приведен следующий код:

Map<String, List<String>> warningMap = (Map<String, List<String>>) warningsOrErrorsMapVariable;
StringBuilder warningMessages = new StringBuilder(size?!);

for (Iterator<String> keyIterator = warningMap.keySet().iterator(); keyIterator.hasNext();) {
    Object key = keyIterator.next();
    if (!keyIterator.hasNext()) {
        isLastKey = true;
    }

    List<String> values = warningMap.get(key);

    if (values != null) {
        isLastValue = false;
        for (Iterator<String> valueIterator = values.iterator(); valueIterator.hasNext();) {
            String message = valueIterator.next();
            if (!valueIterator.hasNext()) {
                isLastValue = true;
            }               

            warningMessages.append(message);
            if (!(isLastKey && isLastValue)) {
                warningMessages.append(NEW_LINE);   
            }
        }
    }
}
return warningMessages.toString();

Как лучше всего объявлять StringBuiler с правильным размером для составных структур?

Один из вариантов - прогнозирование размера всей карты на mapElems* listElems * predictedListElemSize, другой - посещение каждого элемента и получение точного размера, но оба они требуют повторения по всей карте дважды - во-первых, чтобы получить размер, во-вторых, чтобыполучить значения String добавленные в буферСтоит ли оно того?

Неужели весь "вычислительный размер элементов" будет занимать больше времени, чем изменение размера буфера компоновщика при необходимости?

Ответы [ 3 ]

3 голосов
/ 04 февраля 2012

Для меня это звучит как преждевременная оптимизация.Размер по умолчанию на самом деле не лучший выбор в большинстве случаев (редко вы используете StringBuilder для строк короче 16 символов), поэтому сделайте простое предположение.

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

В вашем коде я бы предпочел сосредоточиться на неэффективном цикле над Map: вы перебираете keySet() и выбираете значения почти для каждогоитерация с использованием Map.get().Повторяйте, используя entrySet() с самого начала!

См. Также

1 голос
/ 04 февраля 2012

Да, умножения размера карты на ожидаемый (предполагаемый) средний размер списка и на ожидаемую (предполагаемую) среднюю длину строки должно быть достаточно.

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

Я сомневаюсь, что именно здесь у вас возникнут проблемы с производительностью.

Примечание: ваш код будет проще, если вы добавляете новую строку перед каждым элементом, кроме первого, и используете синтаксис цикла foreach. Вы должны также выполнить итерацию по entrySet() карты, а не выполнять итерации по ключам и выполнять поиск на каждой итерации:

boolean isFirst = true;
for (Map.Entry<String, List<String>> entry : warningMap.entrySet()) {
    List<String> warnings = entry.getValue();
    if (warnings != null) {
        for (String warning : warnings) {
            if (!isFirst) {
                warningMessages.append(NEW_LINE);
            }
            isFirst = false;
            warningMessages.append(warning);
        }
    }
}
1 голос
/ 04 февраля 2012

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

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

  • ваша программа работает с жесткими ограничениями памяти, и для расширения StringBuilder будет недостаточно памяти, или

  • вы профилировали свое приложение, и дополнительная работа, связанная с расширением StringBuilder, является критическим узким местом производительности.

В противном случае попытка получить правильный размер является ненужной оптимизацией.


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

Разве весь «размер вычислений элементов» будет занимать больше времени, чем изменение размера буфера компоновщика при необходимости?

Может быть или не быть. (Я был бы склонен думать не в вашем примере, поскольку String.length() дешево и O(1).) Но я пытаюсь подчеркнуть, что вы даже не должны думать об этом если у вас нет четких доказательств того, что оптимизация (и дополнительная сложность кода) действительно необходимы.

...