String.replaceAll 比自己完成工作要慢得多
Posted
技术标签:
【中文标题】String.replaceAll 比自己完成工作要慢得多【英文标题】:String.replaceAll is considerably slower than doing the job yourself 【发布时间】:2011-09-09 21:10:33 【问题描述】:我有一段旧代码可以在字符串中查找和替换标记。
它接收from
和to
对的映射,遍历它们并针对每一对,遍历目标字符串,使用indexOf()
查找from
,并将其替换为值的to
。它在StringBuffer
上完成所有工作并最终返回String
。
我用这一行替换了该代码:replaceAll("[,. ]*", "");
我还进行了一些比较性能测试。
在比较 1,000,000
迭代时,我得到了:
旧代码:1287ms 新代码:4605ms
3 倍!
然后我尝试将其替换为 3 次调用 replace
:replace(",", "");
replace(".", "");
replace(" ", "");
结果如下:
旧代码:1295 新代码:3524
2 倍!
知道为什么replace
和replaceAll
效率这么低吗?我可以做点什么让它更快吗?
编辑:感谢所有答案 - 主要问题确实是 [,. ]*
没有做我想做的事。将其更改为 [,. ]+
几乎等于非基于正则表达式的解决方案的性能。
使用预编译的正则表达式会有所帮助,但效果不佳。 (这是一个非常适用于我的问题的解决方案。
测试代码:Replace string with Regex: [,. ]*Replace string with Regex: [,. ]+Replace string with Regex: [,. ]+ and Pre-Compiled Pattern
【问题讨论】:
哦,正则表达式的乐趣... 【参考方案1】:虽然使用正则表达式会带来一些性能影响,但应该没有那么糟糕。
请注意,使用String.replaceAll()
将编译正则表达式每次您调用它。
您可以通过显式使用Pattern
对象来避免这种情况:
Pattern p = Pattern.compile("[,. ]+");
// repeat only the following part:
String output = p.matcher(input).replaceAll("");
另请注意,使用+
代替*
可以避免替换空字符串,因此也可能加快处理速度。
【讨论】:
@Lukas:它不会改变 what 已完成(在这种情况下,替换是空字符串)。它可能会改变性能特征,但我不知道省略+
是更快还是更慢。
@Joachim,有趣的是,通过对您的解决方案进行简单的基准测试,[]+
似乎最快 (1x),[]
在中间 (1.5x),[]*
最慢 ( 2x)。
这个答案假设他收到的地图被一遍又一遍地重复使用。
@Joachim, []* 匹配空字符串“”。因此,对于每个字符,都会找到匹配的 "" 并替换为 ""。这会导致它变慢。您可以通过执行 ....replaceAll("$$$$") 并观察输出来测试它。
@SJuan76:我很清楚这一点(我已经在回答中解释过)。我唯一不知道的是replaceAll("[,. ]+", "")
会比replaceAll("[,. ]", "")
慢还是快。【参考方案2】:
replace
和 replaceAll
在内部使用 regex,与 StringUtils.replace(..)
相比,在大多数情况下会给出严重的 performance impact。
String.replaceAll()
:
public String replaceAll(String regex, String replacement)
return Pattern.compile(regex).matcher(this ).replaceAll(
replacement);
String.replace()
在下面使用 Pattern.compile。
public String replace(CharSequence target, CharSequence replacement)
return Pattern.compile(target.toString(), Pattern.LITERAL)
.matcher(this ).replaceAll(
Matcher.quoteReplacement(replacement.toString()));
另见Replace all occurrences of substring in a string - which is more efficient in Java?
【讨论】:
replace()
不使用正则表达式。
@Adeel,String.replace(char, char)
没有。 String.replace(CharSequence, CharSequence)
确实如此。
@Lukas:这些都没有。顺便说一句,replace(char, char)
毫无疑问,因为它不能使用正则表达式,而且 OP 从未使用过它。
@Lukas String.replace(CharSequence, CharSequence)
使用编译模式,是的,但未针对正则表达式编译。
@Johan 是的,这也是一首儿歌的前三个字。 (这也无关紧要)【参考方案3】:
正如我发表的评论 [,. ]* 匹配空字符串“”。因此,字符之间的每个“空格”都与模式匹配。仅在性能上注明,因为您将很多“”替换为“”。
尝试这样做:
Pattern p = Pattern.compile("[,. ]*");
System.out.println(p.matcher("Hello World").replaceAll("$$$");
返回:
H$$$e$$$l$$$o$$$$$$W$$$o$$$r$$$l$$$d$$$!$$$
难怪“手动”操作会更慢!您应该尝试使用 [,. ]+
【讨论】:
谢谢 - 这解释了为什么它如此努力地执行替换【参考方案4】:对于replaceAll("[,. ]*", "")
,这并不奇怪,因为它依赖于正则表达式。正则表达式引擎创建一个自动机,它在输入上运行。预计会有一些开销。
第二种方法(replace(",", "")...
)也在内部使用正则表达式。然而,这里给定的模式是使用Pattern.LITERAL
编译的,因此正则表达式的开销应该可以忽略不计。)在这种情况下,可能是因为Strings
是不可变的(无论你做了多么小的更改,你都会创建一个新字符串),因此不如就地操作字符串的StringBuffers
高效。
【讨论】:
@aiiobe:String.replace(CharSequence, CharSequence)
内部使用正则表达式...
是的,但是Pattern.LITERAL
实际上会关闭所有正则表达式开销。
但这恰恰意味着它没有,你只能将一个正则表达式传递给它,但它不会按预期工作,我想。
不过,LITERAL
模式的编译应该不会太复杂吧? ;-)以上是关于String.replaceAll 比自己完成工作要慢得多的主要内容,如果未能解决你的问题,请参考以下文章
String replaceAll() 与 Matcher replaceAll()(性能差异)
JavaScript------自定义string.replaceAll()方法
Java String ReplaceAll 方法给出非法重复错误?