Який найшвидший спосіб позбавити всіх символів, що не друкуються, з String
Java?
Поки що я пробував і вимірював 138-байтовий, 131-символьний рядок:
- String's
replaceAll()
- найповільніший метод- 517009 результати / сек
- Попередньо скомпілюйте шаблон, а потім використовуйте Matcher's
replaceAll()
- 637836 результатів / сек
- Використовуйте StringBuffer, отримуйте кодові точки, використовуючи
codepointAt()
один за одним, та додайте до StringBuffer- 711946 результатів / сек
- Використовуйте StringBuffer, отримуйте символи, використовуючи
charAt()
один за одним, та додайте до StringBuffer- 1052964 результати / сек
- Попередньо розподіліть
char[]
буфер, отримайте символи, використовуючиcharAt()
один за одним, і заповніть цей буфер, а потім перетворіть назад у рядок- 2022653 результати / сек
- Попередньо розподіліть 2
char[]
буфери - старий і новий, отримайте всі символи для існуючого рядка за один разgetChars()
, перегляньте старий буфер по одному і заповніть новий буфер, а потім перетворіть новий буфер у рядок - моя найшвидша версія- 2502502 результати / сек
- Те ж матеріал з 2 буферів - тільки з використанням
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" безпосередньо дає кращу продуктивність, ніж використання попередньо виділеного статичного 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
клас надає як константу. Розбірка:
Ops / s │ Алгоритм ──────────┼────────────────────────────── 6 535 947 │ Voo1 ──────────┼────────────────────────────── 5350454 │ RatchetFreak2EdStaub1GreyCat1 5 249 343 │ EdStaub1 5002501 │ EdStaub1GreyCat1 4 859 086 │ ArrayOfCharFromStringCharAt 4 295 532 │ RatchetFreak1 4 045 307 │ ArrayOfCharFromArrayOfChar 2790178 │ RatchetFreak2EdStaub1GreyCat2 2 583 311 │ RatchetFreak2 1 274 859 │ StringBuilderChar 1 138 174 │ StringBuilderCodePoint 994 727 │ ArrayOfByteUTF8String 918 611 │ ArrayOfByteUTF8Const 756 086 │ MatcherReplace 598945 │ StringReplaceAll 460 045 │ ArrayOfByteWindows1251
У графічній формі:
(джерело: greycat.ru )
Кілька рядків, 100% рядків містять контрольні символи
Постачальник вихідних рядків заздалегідь згенерував безліч випадкових рядків, використовуючи набір символів (0..127) - таким чином, майже всі рядки містили принаймні один керуючий символ. Алгоритми отримували рядки з цього попередньо сформованого масиву круговим способом.
Ops / s │ Алгоритм ──────────┼────────────────────────────── 2 123 142 │ Voo1 ──────────┼────────────────────────────── 1 782 214 │ EdStaub1 1 776 199 │ EdStaub1GreyCat1 1669628 │ ArrayOfCharFromStringCharAt 1 481481 │ ArrayOfCharFromArrayOfChar 1 460 067 │ RatchetFreak2EdStaub1GreyCat1 1 438 435 │ RatchetFreak2EdStaub1GreyCat2 1 366 494 │ RatchetFreak2 1 349 710 │ RatchetFreak1 893 176 │ ArrayOfByteUTF8String 817 127 │ ArrayOfByteUTF8Const 778899 │ StringBuilderChar 734 754, StringBuilderCodePoint 377 829 │ ArrayOfByteWindows1251 224 140 │ MatcherReplace 211 104 │ StringReplaceAll
У графічній формі:
(джерело: greycat.ru )
Кілька рядків, 1% рядків містять контрольні символи
Так само, як і попередні, але лише 1% рядків було сформовано з керуючими символами - інші 99% було створено за допомогою набору символів [32..127], тому вони взагалі не могли містити контрольні символи. Це синтетичне навантаження найближче до реального застосування цього алгоритму у мене.
Ops / s │ Алгоритм ──────────┼────────────────────────────── 3 711 952 │ Voo1 ──────────┼────────────────────────────── 2 851440 │ EdStaub1GreyCat1 2 455 796 │ EdStaub1 2 426 007 │ ArrayOfCharFromStringCharAt 2 347969 │ RatchetFreak2EdStaub1GreyCat2 2224152 │ 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 501555 │ StringReplaceAll 381 185 │ ArrayOfByteWindows1251
У графічній формі:
(джерело: greycat.ru )
Мені дуже важко визначитися з тим, хто надав найкращу відповідь, але, враховуючи реальний додаток, найкраще рішення дав / надихнув Ед Стауб, я думаю, було б справедливо відзначити його відповідь. Дякуємо за всіх, хто брав у цьому участь, ваш внесок був дуже корисним та безцінним. Не соромтеся запустити тестовий пакет на своїй скриньці та запропонувати ще кращі рішення (робоче рішення JNI, хтось?)
Список літератури
- Репозиторій GitHub із набором тестів