Инициализация StringBuilder для использования кодера UTF-16 - PullRequest
4 голосов
/ 30 мая 2019

Рассмотрим следующий код в Java 11:

StringBuilder sb = new StringBuilder("one");
sb.append("δύο");  // "two"

В первой строке создается StringBuilder, который использует кодировщик Latin1 (один байт на символ).Затем вторая строка заставляет StringBuilder понять, что ему нужно вместо этого использовать кодер UTF16, поэтому он копирует свое текущее содержимое в новый массив перед добавлением новых символов UTF-16.

Класс StringBuilder имеет конструкторПерегрузка, которая принимает начальный аргумент емкости, который разработан, чтобы избежать перераспределения, если вы уже знаете требуемый размер строки, которая будет построена.Но если вы начинаете с английской строки, а затем добавляете иностранную строку, эта конкретная перегрузка конструктора бесполезна, поскольку она все еще перераспределяет байтовый массив.

Есть ли способ создать экземпляр StringBuilder, который использует UTF16 прямо изначать?

Ответы [ 3 ]

3 голосов
/ 30 мая 2019

В версии StringBuilder для Java 11 или Java 12 нет ничего, что могло бы сделать это.

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

Если это будет иметь существенное значение, вы можете реализовать свою собственную версию StringBuilder (расширяя те же интерфейсы для совместимости).

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

1 голос
/ 30 мая 2019

Проведя немного больше исследований в этом направлении, я даю другой ответ на свой вопрос (Переполнение стека говорит, что вполне приемлемо ответить на ваш собственный вопрос.)

Как Славомир говорит StringBuilder инициализируется с Latin1, несмотря ни на что.Предположим, вы пишете в основном на русском, китайском, хинди или греческом языках.Вы хотите создать строку, максимальный размер которой вы уже знаете, поэтому вы используете начальный аргумент емкости:

StringBuilder sb = new StringBuilder(4096);
sb.append("Здравствуйте!");  // Should easily fit in 4 kilobytes, right?

Тем не менее, приведенный выше вызов append отбрасывает буфер 4 КБ, который вы ранее инициализировали, и выделяетновый буфер.Вы создали StringBuilder с начальной емкостью, чтобы избежать перераспределения буфера, но StringBuilder перераспределил его в любом случае.И он перераспределил его, даже если он уже был достаточно большим!

Обходной путь - запустить java с помощью опции JVM -XX:-CompactStrings.

Если вы последовательно используете один изэти языки, тогда ваши строки будут использовать UTF-16 в любом случае, поэтому отключение сжатия строк при запуске уменьшит накладные расходы на проверку каждой строки, которую вы предоставляете, чтобы увидеть, может ли она быть сохранена с использованием кодировки Latin1.

См. также Доклад Хайнца Кабуца в jPrime Bulgaria, 29 мая 2019 года , где он заставляет StringBuilder исчерпать память из-за этой "функции".

1 голос
/ 30 мая 2019

Кажется, что нет очевидного. Если вы хотите повлиять на способ инициализации StringBuffer, я бы предложил создать утилиту 'initializer', которая реализует CharSequence, и использовать соответствующий конструктор StringBuilder. Вы можете общаться с ним по желанию любой длины и содержания, и внутреннее устройство StringBuilder должно быть достаточно умным, чтобы подхватить его.

Хотя, глядя на реализацию OpenJDK 11, кажется, что он взволнован, начиная с Latin1, несмотря ни на что. Некоторая форма перераспределения, кажется, всегда происходит.

...