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

64

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

另外,它们之间的高级API的差异是什么?(不变性、处理NULL值、处理空字符串等)

7个回答

103
根据String.replaceAll文档的说明,对该方法进行调用有以下内容:

str.replaceAll(regex, repl)形式调用该方法会产生与表达式相同的结果

Pattern.compile(regex).matcher(str).replaceAll(repl)
因此,可以预期调用String.replaceAll和显式创建MatcherPattern之间的性能应该是相同的。 编辑 正如在评论中指出的那样,对于从StringMatcher进行单个replaceAll调用来说,不存在性能差异。然而,如果需要执行多个replaceAll调用,则可以保留已编译的Pattern,以便不必每次都执行相对昂贵的正则表达式模式编译。

19
除了下面提到的编译模式所带来的性能损失,如果你使用的是一个恒定不变的正则表达式,那么请将其编译并放入静态常量中。 - james
3
你在结尾处的“因此”评论只适用于一次调用的情况,在这种情况下,性能指标并不重要。如果有多次使用相同正则表达式替换字符串的情况,则 String.replaceAll 比缓存编译的模式慢。 - Jason S
我比较了String.replace()和Matcher.replaceFirst()的性能,发现String版本明显更快。 - Wrench
2
实际上,反复使用 Matcher 是更好的选择。使用 Pattern.compile(...).matcher("ignored input") 创建它,然后使用 theMatcher.reset(theString).replaceAll(...) 进行操作。 - aliteralmind
3
对于@aliteralmind所说的,持有Matcher对象是非线程安全的。我们在生产环境中通过艰难的方式发现了这一点,并导致了一些损坏的XML字符串。请参见http://www.javamex.com/tutorials/regular_expressions/thread_safety.shtml。 - matrixanomaly
显示剩余5条评论

31

String.replaceAll() 的源代码:

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

必须先编译模式——如果你要在短字符串上多次运行相同的模式,那么通过重复使用已编译的 Pattern,性能会更好。


10

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

如果每次都有不同的正则表达式,那么使用 String 类的 replaceAll 就可以了。但如果你要对许多字符串应用相同的正则表达式,创建一个 Pattern 并重复使用它。


1
把你的回答修补成重复我已经说过的话是很无聊的。 - erickson
如果你是在针对我说这句话,我猜测当你发出回答的时候我已经在编辑了... - Jon Skeet
实际上,它是针对coobird的。 - erickson

6

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

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

制作咖啡等:据我所知,String、Pattern和Matcher都没有相关API功能。

编辑:关于处理null值,String和Pattern的文档没有明确说明,但我怀疑它们会抛出NullPointerException,因为它们需要一个字符串。


5
String.replaceAll 的实现告诉您所需了解的一切:
return Pattern.compile(regex).matcher(this).replaceAll(replacement);

(文档也表明了同样的事情。)
虽然我没有检查缓存,但我肯定认为编译一次模式并保持对其的静态引用比每次调用相同模式的Pattern.compile更有效率。如果有缓存,它将是一个小的效率节省 - 如果没有,它可能会是一个大的节省。

5
区别在于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那么大。

0
其他答案已经充分涵盖了OP的性能部分,但是Matcher::replaceAllString::replaceAll之间的另一个区别也是编译自己的Pattern的原因。当您自己编译Pattern时,有一些选项可以修改正则表达式的应用方式。例如:
Pattern myPattern = Pattern.compile(myRegex, Pattern.CASE_INSENSITIVE);

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接