Каков самый быстрый способ удалить все непечатаемые символы из String
в Java?
До сих пор я пробовал и измерял 138-байтовую строку с 131 символом:
- String
replaceAll()
- самый медленный метод- 517009 результатов/сек
- Предварительно скопируйте шаблон, затем используйте Matcher
replaceAll()
- 637836 результатов/сек
- Используйте StringBuffer, получайте кодовые страницы с помощью
codepointAt()
один за другим и добавьте в StringBuffer- 711946 результатов/сек
- Используйте StringBuffer, получайте символы, используя
charAt()
один за другим и добавляйте к StringBuffer- 1052964 результатов/сек
- Предопределить буфер
char[]
, получить символы, используяcharAt()
один за другим и заполнить этот буфер, а затем преобразовать обратно в String- 2022653 результатов/сек
- Предопределить 2
char[]
буфера - старые и новые, получить все символы для существующей строки сразу с помощьюgetChars()
, поочередно перебирать старый буфер и заполнять новый буфер, а затем конвертировать новый буфер в String - моя самая быстрая версия- 2502502 результатов/сек
- Тот же материал с двумя буферами - только с использованием
byte[]
,getBytes()
и с указанием кодировки как "utf-8"- 857485 результатов/сек
- Тот же материал с 2 буферами
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")
?
Update
- Предложение от уловов с храповым механизмом дает впечатляющие результаты 3105590/сек, улучшение на +24%!
- Предложение от Ed Staub дает еще одно улучшение - 3471017 результатов/сек, + 12% по сравнению с предыдущим лучшим.
Обновление 2
Я изо всех сил старался собрать все предлагаемые решения и перекрестные мутации и опубликовал их как небольшую платформу для сравнения производительности в github . В настоящее время он поддерживает 17 алгоритмов. Один из них - "специальный" - алгоритм Voo1 (, предоставленный SO user Voo) использует сложные уловки отражения, тем самым достигая звездных скоростей, но он испортил состояние строк JVM, он сравнивается отдельно.
Вы можете проверить его и запустить, чтобы определить результаты на вашем поле. Здесь резюме результатов, которые я получил на моем. Он специфицирует:
- Debian sid
- Linux 2.6.39-2-amd64 (x86_64)
- Java, установленная из пакета
sun-java6-jdk-6.24-1
, JVM идентифицирует себя как- Java (TM) SE Runtime Environment (сборка 1.6.0_24-b07)
- 64-разрядная серверная виртуальная машина Java HotSpot TM (сборка 19.1-b02, смешанный режим)
Различные алгоритмы показывают в конечном счете разные результаты, учитывая другой набор входных данных. Я провел тест в трех режимах:
Одинаковая строка
Этот режим работает в одной и той же строке, предоставляемой классом StringSource
как константа. Разборки:
Ops / s │ Algorithm ──────────┼────────────────────────────── 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 994 727 │ ArrayOfByteUTF8String 918 611 │ ArrayOfByteUTF8Const 756 086 │ MatcherReplace 598 945 │ StringReplaceAll 460 045 │ ArrayOfByteWindows1251
В набросках: Такая же строчная диаграмма http://www.greycat.ru/img/os-chart-single.png
Несколько строк, 100% строк содержат управляющие символы
Поставщик исходной строки предварительно сгенерировал множество случайных строк с использованием набора символов (0..127) - таким образом, почти все строки содержали хотя бы один управляющий символ. Алгоритмы получили строки из этого предварительно сгенерированного массива в циклическом режиме.
Ops / s │ Algorithm ──────────┼────────────────────────────── 2 123 142 │ Voo1 ──────────┼────────────────────────────── 1 782 214 │ EdStaub1 1 776 199 │ EdStaub1GreyCat1 1 694 628 │ ArrayOfCharFromStringCharAt 1 481 481 │ ArrayOfCharFromArrayOfChar 1 460 067 │ RatchetFreak2EdStaub1GreyCat1 1 438 435 │ RatchetFreak2EdStaub1GreyCat2 1 366 494 │ RatchetFreak2 1 349 710 │ RatchetFreak1 893 176 │ ArrayOfByteUTF8String 817 127 │ ArrayOfByteUTF8Const 778 089 │ StringBuilderChar 734 754 │ StringBuilderCodePoint 377 829 │ ArrayOfByteWindows1251 224 140 │ MatcherReplace 211 104 │ StringReplaceAll
В набросках: Несколько строк, концентрация 100% http://www.greycat.ru/img/os-chart-multi100.png
Несколько строк, 1% строк содержат управляющие символы
То же, что и предыдущее, но только 1% строк было сгенерировано с помощью управляющих символов - остальные 99% были сгенерированы при использовании набора символов [32..127], поэтому они не могли содержать управляющие символы вообще. Эта синтетическая нагрузка ближе всего подходит для реального применения этого алгоритма у меня.
Ops / s │ Algorithm ──────────┼────────────────────────────── 3 711 952 │ Voo1 ──────────┼────────────────────────────── 2 851 440 │ EdStaub1GreyCat1 2 455 796 │ EdStaub1 2 426 007 │ ArrayOfCharFromStringCharAt 2 347 969 │ RatchetFreak2EdStaub1GreyCat2 2 242 152 │ RatchetFreak1 2 171 553 │ ArrayOfCharFromArrayOfChar 1 922 707 │ RatchetFreak2EdStaub1GreyCat1 1 857 010 │ RatchetFreak2 1 023 751 │ ArrayOfByteUTF8String 939 055 │ StringBuilderChar 907 194 │ ArrayOfByteUTF8Const 841 963 │ StringBuilderCodePoint 606 465 │ MatcherReplace 501 555 │ StringReplaceAll 381 185 │ ArrayOfByteWindows1251
В набросках: Несколько строк, концентрация 1% http://www.greycat.ru/img/os-chart-multi1.png
Мне очень трудно решить, кто дал лучший ответ, но, учитывая реальное приложение, лучшее решение было дано/вдохновлено Эд Штаубом, я думаю, было бы справедливо отметить его ответ. Спасибо всем, кто принял участие в этом, ваш вклад был очень полезным и бесценным. Не стесняйтесь запускать тестовый пакет на вашем ящике и предлагать еще лучшие решения (работая над решением JNI, кто-нибудь?).
Ссылки
- репозиторий GitHub с набором тестов