首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >从Java字符串中去除所有不可打印的字符的最快方法?

从Java字符串中去除所有不可打印的字符的最快方法?
EN

Stack Overflow用户
提问于 2018-03-02 06:04:01
回答 2查看 0关注 0票数 0

什么是从StringJava中去除所有不可打印字符的最快方法?

到目前为止,我已经尝试过使用138字节,131字符的字符串进行测量:

  • 字符串的replaceAll()- 最慢的方法
    • 517009结果/秒
  • 预编译模式,然后使用匹配器 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结果/秒

我最好的尝试是以下几点:

代码语言:javascript
复制
    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")

棘轮怪胎的建议产生令人印象深刻的3105590结果/秒的表现,+ 24%的改善!

Ed Staub的建议产生了另一项改进 - 每秒3471017个结果,比之前的最佳结果增加12%。

我尽我所能收集了所有提出的解决方案及其交叉变异,并将它作为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运行时环境(build 1.6.0_24-b07)
    • Java HotSpot(TM)64位服务器虚拟机(构建19.1-b02,混合模式)

给定一组不同的输入数据,不同的算法会显示最终的不同结果。我在3种模式下运行了一个基准测试:

同一个字符串

这种模式在StringSource类提供的同一个单一字符串上作为常量使用。摊牌是:

代码语言:javascript
复制
 Ops / s│算法
──────────┼──────────────────────────────
6 535 947│For1
──────────┼──────────────────────────────
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)字符集预先生成大量随机字符串 - 因此几乎所有字符串都至少包含一个控制字符。算法以循环方式从此预生成阵列接收字符串。

代码语言:javascript
复制
 Ops / s│算法
──────────┼──────────────────────────────
2 123 142│对于.1
──────────┼──────────────────────────────
1 782 214│EdStaub1
1 776 199│EdStaub1GreyCat1
1 694 628│ArrayOfCharFromStringCharAt
1 481 481│ArrayOfCharFromArrayOfChar
1 460 067│RatchetFreak2EdStaub1GreyCat1
1 43​​8 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

以图表形式: http://www.greycat.ru/img/os-chart-multi100.png

多个字符串,1%的字符串包含控制字符

与之前相同,但只有1%的字符串是由控制字符生成的 - 其他99%是在使用[32..127]字符集时生成的,因此它们根本不能包含控制字符。这个综合负载在我的地方最接近这个算法的实际应用。

代码语言:javascript
复制
 Ops / s│算法
──────────┼──────────────────────────────
3 711 952│For1
──────────┼──────────────────────────────
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

以图表形式: http://www.greycat.ru/img/os-chart-multi1.png

我很难决定谁提供了最好的答案,但考虑到现实世界应用程序的最佳解决方案是由Ed Staub给出/启发的,我想这是公平的标记他的答案。感谢参与此事的所有人,您的意见非常有帮助和宝贵。随意在你的机器上运行测试套件,并提出更好的解决方案(任何人都可以使用JNI解决方案)。

EN

回答 2

Stack Overflow用户

发布于 2018-03-02 14:08:00

如果将此方法嵌入到不是跨线程共享的类中是合理的,则可以重新使用该缓冲区:

代码语言:javascript
复制
char [] oldChars = new char[5];

String stripControlChars(String s)
{
    final int inputLen = s.length();
    if ( oldChars.length < inputLen )
    {
        oldChars = new char[inputLen];
    }
    s.getChars(0, inputLen, oldChars, 0);

等等...

这是一个巨大的胜利 - 20%左右,因为我了解目前的最佳案例。

如果这是用于潜在的大字符串,并且内存“泄漏”是一个问题,则可以使用弱引用。

票数 0
EN

Stack Overflow用户

发布于 2018-03-02 15:49:02

使用1个字符数组可能会更好一些

代码语言:javascript
复制
int length = s.length();
char[] oldChars = new char[length];
s.getChars(0, length, oldChars, 0);
int newLen = 0;
for (int j = 0; j < length; j++) {
    char ch = oldChars[j];
    if (ch >= ' ') {
        oldChars[newLen] = ch;
        newLen++;
    }
}
s = new String(oldChars, 0, newLen);

我避免了重复的呼叫 s.length();

另一个可能起作用的微型优化是

代码语言:javascript
复制
int length = s.length();
char[] oldChars = new char[length+1];
s.getChars(0, length, oldChars, 0);
oldChars[length]='\0';//avoiding explicit bound check in while
int newLen=-1;
while(oldChars[++newLen]>=' ');//find first non-printable,
                       // if there are none it ends on the null char I appended
for (int  j = newLen; j < length; j++) {
    char ch = oldChars[j];
    if (ch >= ' ') {
        oldChars[newLen] = ch;//the while avoids repeated overwriting here when newLen==j
        newLen++;
    }
}
s = new String(oldChars, 0, newLen);
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/-100007495

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档