关于java:String.replaceAll比自己完成这项工作要慢得多

String.replaceAll is considerably slower than doing the job yourself

我有一段旧代码,用于查找和替换字符串中的标记。

它接收fromto对的映射,对其进行迭代,并针对每个对,迭代目标字符串,使用indexOf()查找from,并将其替换为。它在StringBuffer上完成所有工作,并最终返回String

我用以下代码替换了该代码:replaceAll("[,. ]*","");
我进行了一些比较性能测试。
比较1,000,000迭代时,我得到了:

Old Code: 1287ms
New Code: 4605ms

长3倍!

然后,我尝试用3个对replace的调用替换它:
replace(",","");
replace(".","");
replace("","");

结果如下:

Old Code: 1295
New Code: 3524

长2倍!

知道为什么replacereplaceAll如此低效吗?我可以做些什么使它更快吗?

编辑:感谢所有的答案-主要的问题确实是[,. ]*没有执行我想要的操作。将其更改为[,. ]+几乎等于基于非Regex的解决方案的性能。
使用预编译的正则表达式虽然有所帮助,但却是微不足道的。 (这是一个非常适合我的问题的解决方案。

测试代码:
用正则表达式替换字符串:[,。 ] *
用正则表达式替换字符串:[,。 ] +
用正则表达式替换字符串:[,。 ] +和预编译模式


尽管使用正则表达式会给性能带来一些影响,但它不应该那么糟糕。

注意,每次调用String.replaceAll()时,都会编译正则表达式。

您可以通过显式使用Pattern对象来避免这种情况:

1
2
3
4
Pattern p = Pattern.compile("[,. ]+");

// repeat only the following part:
String output = p.matcher(input).replaceAll("");

还要注意,使用+而不是*可以避免替换空字符串,因此也可以加快处理速度。


replacereplaceAll在内部使用正则表达式,与例如StringUtils.replace(..)相比,在大多数情况下会对性能产生严重影响。

String.replaceAll()

1
2
3
4
public String replaceAll(String regex, String replacement) {
        return Pattern.compile(regex).matcher(this ).replaceAll(
             replacement);
}

String.replace()在下面使用Pattern.compile。

1
2
3
4
5
public String replace(CharSequence target, CharSequence replacement) {
  return Pattern.compile(target.toString(), Pattern.LITERAL)
         .matcher(this ).replaceAll(
           Matcher.quoteReplacement(replacement.toString()));
}

另请参阅替换字符串中所有出现的子字符串-在Java中更有效吗?


正如我发表的评论[,。 ] *匹配空字符串""。因此,字符之间的每个"空格"都与模式匹配。仅在性能中会指出这一点,因为您要替换很多""。

尝试这样做:

1
2
Pattern p = Pattern.compile("[,. ]*");
System.out.println(p.matcher("Hello World").replaceAll("$$$");

它返回:

H $$ e $$$$$$ o $$$$$ W $$ o $$ r $$$ l $$$ d $$!$$$

难怪"手工"做起来会比较慢!您应该尝试使用[,。 ] +


当涉及到replaceAll("[,. ]*","")时,它并不依赖于正则表达式,因此并不令人惊讶。正则表达式引擎创建一个自动机,并在输入上运行。预计会有一些开销。

第二种方法(replace(",","")...)在内部也使用正则表达式。但是,此处给定的模式使用Pattern.LITERAL进行编译,因此正则表达式的开销可以忽略不计。)在这种情况下,可能是由于Strings是不可变的(无论您进行的很小的更改,您都会创建一个新字符串) ),因此效率不如StringBuffers来就地操纵字符串。