Рассмотрим следующий код в Java 11:
StringBuilder sb = new StringBuilder("one");
sb.append("δύο"); // "two"
Первая строка создает StringBuilder
, который использует кодировщик Latin1 (один байт на символ). Затем вторая строка заставляет StringBuilder понять, что вместо этого ему нужно использовать кодировщик UTF16, поэтому он копирует свое текущее содержимое в новый массив перед добавлением новых символов UTF-16.
Класс StringBuilder имеет перегрузку конструктора, которая принимает аргумент начальной емкости, что позволяет избежать перераспределения, если вы уже знаете требуемый размер строящейся строки. Но если вы начинаете с английской строки, а затем добавляете иностранную строку, эта конкретная перегрузка конструктора бесполезна, поскольку она все еще перераспределяет массив байтов.
Есть ли способ создать экземпляр StringBuilder, который с самого начала использует UTF16?
Кажется, что очевидного нет. Если вы хотите повлиять на способ инициализации StringBuffer, я бы предложил создать утилиту «инициализатор», которая реализует CharSequence
и использовать соответствующий конструктор StringBuilder. Вы можете передать любую длину и содержание char, которое вы пожелаете, и внутренности StringBuilder должны быть достаточно умными, чтобы уловить это.
Однако, глядя на реализацию OpenJDK 11, кажется, что он одержим тем, чтобы начать с Latin1, несмотря ни на что. Некоторая форма перераспределения, кажется, происходит всегда.
Спасибо - интересная информация.
В версии StringBuilder
для Java 11 или Java 12 нет ничего, что могло бы сделать это.
Настоящая проблема заключается в том, насколько важен для вас прирост производительности, который вы можете получить от этого. Профилируйте свое приложение, чтобы узнать, значительно ли это нежелательное перераспределение влияет на производительность в общем и целом вашего приложения.
Если это будет иметь существенное значение, вы можете реализовать свою собственную версию StringBuilder
(расширив те же интерфейсы для совместимости).
В качестве альтернативы, если вы готовы ждать, вы можете загрузить исходный код OpenJDK и разработать / собрать / протестировать расширение для StringBuilder
... и представить его как патч для рассмотрения. (Если вы включили тесты, которые продемонстрировали явное преимущество в производительности, это увеличило бы шансы на включение.)
Вопрос в том, действительно ли они хотят включить это в JDK. Компактные строки — это деталь реализации, и введение общедоступного метода для них меняет это.
Проведя немного больше исследований по этому вопросу, я даю еще один ответ на свой вопрос (переполнение стека говорит, что вполне приемлемо ответить на ваш собственный вопрос).
Как Славомир говорит, 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 исчерпать память из-за этой «функции».
альтернатива в одну сторону
sb.append(new String("δύο","UTF-16"))