String replaceAll() 与 Matcher replaceAll()(性能差异)

Posted

技术标签:

【中文标题】String replaceAll() 与 Matcher replaceAll()(性能差异)【英文标题】:String replaceAll() vs. Matcher replaceAll() (Performance differences) 【发布时间】:2010-11-30 19:38:19 【问题描述】:

String.replaceAll() 和 Matcher.replaceAll()(在从 Regex.Pattern 创建的 Matcher 对象上)在性能方面是否存在已知差异?

另外,两者在高级 API 上的区别是什么? (不变性、处理 NULL、处理空字符串等)

【问题讨论】:

【参考方案1】:

根据String.replaceAll 的文档,关于调用方法有以下说法:

这个方法的调用 表格str.replaceAll(regex, repl) 产生与 表达

Pattern.compile(regex).matcher(str).replaceAll(repl)

因此,可以预期调用String.replaceAll 和显式创建MatcherPattern 之间的性能应该相同。

编辑

正如 cmets 中所指出的,从 StringMatcherreplaceAll 的单个调用将不存在性能差异,但是,如果需要对 @ 执行多个调用987654334@,人们会认为保留已编译的Pattern 是有益的,因此不必每次都执行相对昂贵的正则表达式模式编译。

【讨论】:

除了,如下所述,模式编译的性能损失。如果您使用的是常量正则表达式,请将其编译并粘贴到静态常量中。 您最后的“因此”评论仅适用于 1 次通话,在这种情况下,性能指标确实不相关。如果使用相同的正则表达式重复调用 replaceAll,则 String.replaceAll 比缓存已编译的模式要慢。 有谁知道regex 字符串是否是静态的,是否有足够聪明的javac 编译器能够确定Pattern 对象也可以是静态的,并自动在生成的字节码中构建一个静态字段?听起来是提高代码性能同时提高可读性的好方法。 其实,反复使用,拿着Matcher更好。用Pattern.compile(...).matcher("ignored input") 创建它,然后用theMatcher.reset(theString).replaceAll(...) 使用它 @alliteralmind FWIW 持有 Matcher 不是线程安全的。我们在生产中发现了这一点,并导致了一些损坏的 XML 字符串。见javamex.com/tutorials/regular_expressions/thread_safety.shtml【参考方案2】:

String.replaceAll()的源码:

public String replaceAll(String regex, String replacement) 
    return Pattern.compile(regex).matcher(this).replaceAll(replacement);

它必须首先编译模式 - 如果您要在短字符串上使用相同的模式多次运行它,如果您重用一个已编译的模式,性能会好得多。

【讨论】:

【参考方案3】:

主要区别在于,如果您保留用于生成MatcherPattern,则可以避免每次使用正则表达式时都重新编译它。通过String,您无法像这样“缓存”。

如果您每次都有不同的正则表达式,使用String 类的replaceAll 就可以了。如果您将相同的正则表达式应用于多个字符串,请创建一个 Pattern 并重复使用它。

【讨论】:

修补你的答案以重复我已经说过的话是蹩脚的。 如果出于某种原因这是针对我的,我怀疑在您发布答案时我已经在编辑... 其实是针对coobird的。【参考方案4】:

不变性/线程安全:编译的模式是不可变的,匹配器不是。 (见Is Java Regex Thread Safe?)

处理空字符串:replaceAll 应该优雅地处理空字符串(它不会匹配空输入字符串模式)

煮咖啡等:我上次听说,String、Pattern 和 Matcher 都没有任何 API 功能。

编辑:至于处理 NULL,String 和 Pattern 的文档没有明确说明,但我怀疑他们会抛出 NullPointerException,因为他们期望 String。

【讨论】:

【参考方案5】:

String.replaceAll 的实现告诉你你需要知道的一切:

return Pattern.compile(regex).matcher(this).replaceAll(replacement);

(文档也是这样说的。)

虽然我没有检查缓存,但我当然希望编译一个模式一次并保持对它的静态引用比调用 Pattern.compile 每个都使用相同的模式更有效时间。如果有缓存,它会节省一点效率 - 如果没有,它可能会很大。

【讨论】:

【参考方案6】:

不同之处在于 String.replaceAll() 每次调用时都会编译正则表达式。 .NET 的静态 Regex.Replace() 方法没有等效的方法,它会自动缓存已编译的正则表达式。通常,replaceAll() 是您只执行一次的操作,但如果您要使用相同的正则表达式重复调用它,尤其是在循环中,您应该创建一个 Pattern 对象并使用 Matcher 方法。

您也可以提前创建 Matcher,并使用它的 reset() 方法为每次使用重新定位它:

Matcher m = Pattern.compile(regex).matcher("");
for (String s : targets)

  System.out.println(m.reset(s).replaceAll(repl));

当然,重用 Matcher 的性能优势远不及重用 Pattern。

【讨论】:

【参考方案7】:

其他答案充分涵盖了 OP 的性能部分,但 Matcher::replaceAllString::replaceAll 之间的另一个区别也是编译您自己的 Pattern 的原因。当您自己编译 Pattern 时,可以使用诸如标志之类的选项来修改正则表达式的应用方式。例如:

Pattern myPattern = Pattern.compile(myRegex, Pattern.CASE_INSENSITIVE);

Matcher 将应用您在调用 Matcher::replaceAll 时设置的所有标志。

您还可以设置其他标志。大多数情况下,我只是想指出PatternMatcher API 有很多选项,这是超越简单String::replaceAll 的主要原因

【讨论】:

以上是关于String replaceAll() 与 Matcher replaceAll()(性能差异)的主要内容,如果未能解决你的问题,请参考以下文章

String replace() 和 replaceAll() 的区别

为js的String对象添加replaceall属性

JavaScript------自定义string.replaceAll()方法

Java String.replaceAll()方法

string replace和replaceAll的区别

String系列之replaceAll方法替换.