Каков самый быстрый способ удалить все непечатаемые символы из 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 с набором тестов