Каков самый быстрый способ удалить все непечатаемые символы из a String
в Java?
До сих пор я пробовал и измерял 138-байтовую 131-символьную строку:
- String
replaceAll()
- самый медленный метод- 517009 результатов / сек
- Предварительно скомпилируйте шаблон, затем используйте Matcher's
replaceAll()
- 637836 результатов / сек
- Используйте StringBuffer, получайте кодовые точки, используя
codepointAt()
одну за другой, и добавляйте в StringBuffer- 711946 результатов / сек
- Используйте StringBuffer, получайте символы
charAt()
по одному и добавляйте в StringBuffer- 1052964 результатов / сек
- Предварительно выделить
char[]
буфер, получить символы, используяcharAt()
один за другим, и заполнить этот буфер, затем преобразовать обратно в String- 2022653 результатов / сек
- Предварительно выделите 2
char[]
буфера - старый и новый, получите все символы для существующей String сразу, используяgetChars()
, перебирайте старый буфер один за другим и заполняйте новый буфер, затем конвертируйте новый буфер в String - моя собственная самая быстрая версия- 2502502 результатов / сек
- То же материал с 2 буферов - только с использованием
byte[]
,getBytes()
и с указанием кодировки , как «UTF-8»- 857485 результатов / сек
- То же самое с двумя
byte[]
буферами, но указав кодировку как константуCharset.forName("utf-8")
- 791076 результатов / сек
- То же самое с 2
byte[]
буферами, но указание кодировки как 1-байтовой локальной кодировки (едва ли разумная вещь)- 370164 результатов / сек
Моя лучшая попытка была следующей:
char[] oldChars = new char[s.length()];
s.getChars(0, s.length(), oldChars, 0);
char[] newChars = new char[s.length()];
int newLen = 0;
for (int j = 0; j < s.length(); j++) {
char ch = oldChars[j];
if (ch >= ' ') {
newChars[newLen] = ch;
newLen++;
}
}
s = new String(newChars, 0, newLen);
Есть мысли, как сделать это еще быстрее?
Бонусные баллы за ответ на очень странный вопрос: почему использование имени кодировки «utf-8» напрямую дает лучшую производительность, чем использование предварительно выделенной static const Charset.forName("utf-8")
?
Обновить
- Предложение от храповика дает впечатляющие результаты 3105590 результатов / сек, улучшение + 24%!
- Предложение Эда Стауба дает еще одно улучшение - 3471017 результатов / сек, что на + 12% по сравнению с предыдущим лучшим результатом.
Обновление 2
Я изо всех сил старался собрать все предлагаемые решения и их перекрестные мутации и опубликовал их как небольшую платформу для тестирования производительности на github . В настоящее время он поддерживает 17 алгоритмов. Один из них является «специальным» - алгоритм Voo1 ( предоставленный пользователем SO Voo ) использует замысловатые трюки с отражением, что позволяет достичь звездных скоростей, но он портит состояние строк JVM, поэтому он тестируется отдельно.
Вы можете проверить это и запустить, чтобы определить результаты на вашем ящике. Вот краткое изложение моих результатов. Это спецификации:
- Debian sid
- Linux 2.6.39-2-amd64 (x86_64)
- Java, установленная из пакета
sun-java6-jdk-6.24-1
, JVM идентифицирует себя как- Среда выполнения Java (TM) SE (сборка 1.6.0_24-b07)
- 64-разрядная серверная виртуальная машина Java HotSpot (TM) (сборка 19.1-b02, смешанный режим)
Различные алгоритмы показывают в конечном итоге разные результаты при разном наборе входных данных. Я провел тест в 3 режимах:
Та же единственная строка
Этот режим работает с той же единственной строкой, предоставленной StringSource
классом в качестве константы. Разборки таковы:
Операции / с │ Алгоритм ────────────────────────────────────────── 6 535 947 │ Voo1 ────────────────────────────────────────── 5 350 454 │ RatchetFreak2EdStaub1GreyCat1 5 249 343 │ EdStaub1 5 002 501 │ EdStaub1GreyCat1 4 859 086 │ ArrayOfCharFromStringCharAt 4 295 532 │ RatchetFreak1 4 045 307 │ ArrayOfCharFromArrayOfChar 2 790 178 │ RatchetFreak2EdStaub1GreyCat2 2 583 311 │ RatchetFreak2 1 274 859 │ StringBuilderChar 1 138 174 │ StringBuilderCodePoint 994727 │ ArrayOfByteUTF8String 918 611 │ ArrayOfByteUTF8Const 756086 │ Матчер Заменить 598945 │ StringReplaceAll 460 045 │ ArrayOfByteWindows1251
В виде диаграммы:
(источник: greycat.ru )
Несколько строк, 100% строк содержат управляющие символы
Поставщик исходной строки предварительно сгенерировал множество случайных строк с использованием набора символов (0..127) - таким образом, почти все строки содержат хотя бы один управляющий символ. Алгоритмы получали строки из этого предварительно сгенерированного массива циклически.
Операции / с │ Алгоритм ────────────────────────────────────────── 2 123 142 │ Voo1 ────────────────────────────────────────── 1 782 214 │ EdStaub1 1 776199 │ EdStaub1GreyCat1 1694628 │ ArrayOfCharFromStringCharAt 1 481 481 │ ArrayOfCharFromArrayOfChar 1 460 067 │ RatchetFreak2EdStaub1GreyCat1 1 438 435 │ RatchetFreak2EdStaub1GreyCat2 1 366 494 │ RatchetFreak2 1 349 710 │ RatchetFreak1 893 176 │ ArrayOfByteUTF8String 817127 │ ArrayOfByteUTF8Const 778 089 │ StringBuilderChar 734 754 │ StringBuilderCodePoint 377 829 │ ArrayOfByteWindows1251 224 140 │ Матчер Заменить 211 104 │ StringReplaceAll
В виде диаграммы:
(источник: greycat.ru )
Несколько строк, 1% строк содержат управляющие символы
То же, что и предыдущее, но только 1% строк был сгенерирован с управляющими символами - остальные 99% были сгенерированы с использованием набора символов [32..127], поэтому они вообще не могли содержать управляющие символы. Эта синтетическая нагрузка является наиболее близкой к реальному применению этого алгоритма у меня.
Операции / с │ Алгоритм ────────────────────────────────────────── 3 711 952 │ Voo1 ────────────────────────────────────────── 2 851 440 │ EdStaub1GreyCat1 2 455 796 │ EdStaub1 2 426007 │ ArrayOfCharFromStringCharAt 2 347 969 │ RatchetFreak2EdStaub1GreyCat2 2 242 152 │ RatchetFreak1 2 171 553 │ ArrayOfCharFromArrayOfChar 1922707 │ RatchetFreak2EdStaub1GreyCat1 1 857 010 │ RatchetFreak2 1 023 751 │ ArrayOfByteUTF8String 939 055 │ StringBuilderChar 907 194 │ ArrayOfByteUTF8Const 841 963 │ StringBuilderCodePoint 606 465 │ Матчер Заменить 501 555 │ StringReplaceAll 381 185 │ ArrayOfByteWindows1251
В виде диаграммы:
(источник: greycat.ru )
Мне очень сложно решить, кто дал лучший ответ, но, учитывая, что лучшее решение для реального приложения было предложено / вдохновлено Эдом Стаубом, я думаю, было бы справедливо отметить его ответ. Спасибо всем, кто принял в этом участие, ваш вклад был очень полезным и бесценным. Не стесняйтесь запускать набор тестов на своем компьютере и предлагать еще лучшие решения (рабочее решение JNI, кто-нибудь?).
Рекомендации
- Репозиторий GitHub с набором тестов