60

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

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

4

7 回答 7

96

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

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

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

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

编辑

正如评论中所指出的那样,对于replaceAllfromString或的单个调用,性能差异是不存在的Matcher,但是,如果需要执行对 的多次调用replaceAll,人们会期望保留一个已编译的Pattern,所以不需要每次都执行相对昂贵的正则表达式模式编译。

于 2009-09-23T16:06:45.203 回答
30

源代码String.replaceAll()

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

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

于 2009-09-23T16:08:11.270 回答
10

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

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

于 2009-09-23T16:07:40.727 回答
6

不变性/线程安全:编译的模式是不可变的,匹配器不是。(请参阅Java 正则表达式线程安全吗?

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

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

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

于 2009-09-23T16:15:00.313 回答
5

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

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

(和文档说同样的话。)

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

于 2009-09-23T16:05:55.573 回答
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。

于 2009-09-23T16:35:53.923 回答
0

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

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

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

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

于 2016-12-01T18:33:38.297 回答