String
Javaで印刷できない文字をすべて削除する最速の方法は何ですか?
これまでのところ、138バイト、131文字の文字列で試して測定しました。
- 文字列
replaceAll()
-最も遅いメソッド- 517009結果/秒
- パターンをプリコンパイルしてから、Matcherを使用します
replaceAll()
- 637836結果/秒
- StringBufferを使用し、
codepointAt()
1つずつ使用してコードポイントを取得し、StringBufferに追加します- 711946結果/秒
- StringBufferを使用し、
charAt()
1つずつ使用して文字を取得し、StringBufferに追加します- 1052964結果/秒
char[]
バッファを事前に割り当て、charAt()
1つずつ使用して文字を取得し、このバッファを埋めてから、文字列に変換し直します- 2022653結果/秒
char[]
古いバッファと新しいバッファの2つのバッファを事前に割り当て、を使用して既存の文字列のすべての文字を一度に取得しgetChars()
、古いバッファを1つずつ繰り返し、新しいバッファを埋めてから、新しいバッファを文字列に変換します-私自身の最速バージョン- 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」文字セット名を使用すると、事前に割り当てられたstaticconstを使用するよりもパフォーマンスが直接向上するのはなぜCharset.forName("utf-8")
ですか。
更新
- ラチェットフリークからの提案は、印象的な3105590の結果/秒のパフォーマンス、+ 24%の改善をもたらします!
- Ed Staubからの提案により、さらに別の改善がもたらされます-3471017の結果/秒、以前のベストよりも+ 12%。
アップデート2
私は、提案されたすべてのソリューションとその相互変異を収集するために最善を尽くし、それをgithubで小さなベンチマークフレームワークとして公開しました。現在、17のアルゴリズムを備えています。それらの1つは「特別」です-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)
- Java HotSpot(TM)64ビットサーバーVM(ビルド19.1-b02、混合モード)
異なるアルゴリズムは、異なる入力データのセットが与えられた場合、最終的に異なる結果を示します。私は3つのモードでベンチマークを実行しました:
同じ単一の文字列
このモードは、StringSource
クラスによって定数として提供されるのと同じ単一の文字列で機能します。対決は次のとおりです。
Ops /s│アルゴリズム ──────────┼────────────────────────────── 6535947│Voo1 ──────────┼────────────────────────────── 5350454│RatchetFreak2EdStaub1GreyCat1 5249343│EdStaub1 50002501│EdStaub1GreyCat1 4859086│ArrayOfCharFromStringCharAt 4295532│RatchetFreak1 4045307│ArrayOfCharFromArrayOfChar 2790178│RatchetFreak2EdStaub1GreyCat2 2583311│RatchetFreak2 1274859│StringBuilderChar 1138174│StringBuilderCodePoint 994727│ArrayOfByteUTF8String 918611│ArrayOfByteUTF8Const 756086│MatcherReplace 598945│StringReplaceAll 460045│ArrayOfByteWindows1251
グラフ形式:(
出典:greycat.ru)
複数の文字列、文字列の100%に制御文字が含まれています
ソース文字列プロバイダーは、(0..127)文字セットを使用して大量のランダム文字列を事前に生成しました。したがって、ほとんどすべての文字列に少なくとも1つの制御文字が含まれていました。アルゴリズムは、この事前生成された配列からラウンドロビン方式で文字列を受け取りました。
Ops /s│アルゴリズム ──────────┼────────────────────────────── 2123142│Voo1 ──────────┼────────────────────────────── 1782214│EdStaub1 1776199│EdStaub1GreyCat1 1694628│ArrayOfCharFromStringCharAt 1481481│ArrayOfCharFromArrayOfChar 1460067│RatchetFreak2EdStaub1GreyCat1 1438435│RatchetFreak2EdStaub1GreyCat2 1366494│RatchetFreak2 1349710│RatchetFreak1 893176│ArrayOfByteUTF8String 817127│ArrayOfByteUTF8Const 778089│StringBuilderChar 734754│StringBuilderCodePoint 377829│ArrayOfByteWindows1251 224140│MatcherReplace 211104│StringReplaceAll
グラフ形式:(
出典:greycat.ru)
複数の文字列、文字列の1%に制御文字が含まれています
以前と同じですが、文字列の1%のみが制御文字で生成されました。他の99%は、[32..127]文字セットを使用して生成されたため、制御文字をまったく含めることができませんでした。この合成負荷は、私の場所でこのアルゴリズムの実際のアプリケーションに最も近いものです。
Ops /s│アルゴリズム ──────────┼────────────────────────────── 3711952│Voo1 ──────────┼────────────────────────────── 2851440│EdStaub1GreyCat1 2455796│EdStaub1 2426007│ArrayOfCharFromStringCharAt 2347969│RatchetFreak2EdStaub1GreyCat2 2242152│RatchetFreak1 2171553│ArrayOfCharFromArrayOfChar 1922707│RatchetFreak2EdStaub1GreyCat1 1857010│RatchetFreak2 1023751│ArrayOfByteUTF8String 939055│StringBuilderChar 907194│ArrayOfByteUTF8Const 841963│StringBuilderCodePoint 606465│MatcherReplace 501555│StringReplaceAll 381185│ArrayOfByteWindows1251
グラフ形式:(
出典:greycat.ru)
誰が最良の答えを提供したかを決めるのは非常に難しいですが、実際のアプリケーションを考えると、Ed Staubによって最良の解決策が与えられ、触発されたので、彼の答えをマークするのは公平だと思います。これに参加してくれたすべての人に感謝します。あなたの意見は非常に役に立ち、非常に貴重でした。ボックスでテストスイートを自由に実行して、さらに優れたソリューションを提案してください(実用的なJNIソリューション、誰か?)。
参考文献
- ベンチマークスイートを備えたGitHubリポジトリ