在 Java 中,从 String
中去除所有不可打印字符的最快方法是什么?
到目前为止,我尝试并测量了138字节、131字符的 String:
replaceAll()
-< strong > Slow 方法
replaceAll()
codepointAt()
逐个获取代码点,并附加到 StringBuffer
charAt()
逐个获取字符,并附加到 StringBuffer
char[]
缓冲区,使用 charAt()
逐个获取字符并填充该缓冲区,然后转换回 String
char[]
缓冲区,使用 getChars()
一次性获取现有 String 的所有字符,逐个迭代旧缓冲区并填充新缓冲区,然后将新缓冲区转换为 String-< strong > 我自己的最快版本
byte[]
,getBytes()
和指定编码为“ utf-8”
byte[]
缓冲区,但指定编码为一个常量 Charset.forName("utf-8")
byte[]
缓冲区的情况相同,但是将编码指定为1字节的本地编码(这几乎不是一件明智的事情)
我最好的尝试如下:
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”字符集名称比使用预分配的静态常量 Charset.forName("utf-8")
产生更好的性能?
我已经尽力收集了所有提出的解决方案及其交叉突变,并将其作为 Github 的小型基准测试框架发表。目前它运行着17种算法。其中之一是“特殊”-哇哦算法(由 SO 用户 Voo 提供)采用复杂的反射技巧,从而实现恒星速度,但它混乱了 JVM 字符串的状态,因此它是单独基准测试。
欢迎您检查它,并运行它,以确定您的盒子的结果。这是我得到的结果总结。是说明书:
sun-java6-jdk-6.24-1
安装的,JVM 将自己标识为
不同的算法给出不同的输入数据集,最终显示不同的结果:
这种模式作为常量在 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
以图表形式:
(来源: Greycat.ru)
源字符串提供程序使用(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
以图表形式:
(来源: Greycat.ru)
与前面相同,但是只有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
以图表形式:
(来源: Greycat.ru)
It's very hard for me to decide on who provided the best answer, but given the real-world application best solution was given/inspired by Ed Staub, I guess it would be fair to mark his answer. Thanks for all who took part in this, your input was very helpful and invaluable. Feel free to run the test suite on your box and propose even better solutions (working JNI solution, anyone?).