String.replaceAll() 和 Matcher.replaceAll() (在从 Regex.Pattern 创建的 Matcher 对象上)在性能方面是否存在已知差异?
此外,两者之间的高级 API 差异是什么?(不变性、处理 NULL、处理空字符串等)
String.replaceAll() 和 Matcher.replaceAll() (在从 Regex.Pattern 创建的 Matcher 对象上)在性能方面是否存在已知差异?
此外,两者之间的高级 API 差异是什么?(不变性、处理 NULL、处理空字符串等)
根据 的文档String.replaceAll
,关于调用该方法有以下说明:
调用这种形式的方法会
str.replaceAll(regex, repl)
产生与表达式完全相同的结果Pattern.compile(regex).matcher(str).replaceAll(repl)
因此,可以预期调用String.replaceAll
和显式创建Matcher
and之间的性能Pattern
应该是相同的。
编辑
正如评论中所指出的那样,对于replaceAll
fromString
或的单个调用,性能差异是不存在的Matcher
,但是,如果需要执行对 的多次调用replaceAll
,人们会期望保留一个已编译的Pattern
,所以不需要每次都执行相对昂贵的正则表达式模式编译。
源代码String.replaceAll()
:
public String replaceAll(String regex, String replacement) {
return Pattern.compile(regex).matcher(this).replaceAll(replacement);
}
它必须首先编译模式 - 如果您要在短字符串上使用相同的模式多次运行它,如果您重用一个已编译的模式,性能会好得多。
主要区别在于,如果您坚持Pattern
使用 used 来生成Matcher
,则可以避免每次使用时都重新编译正则表达式。通过String
,您将无法像这样“缓存”。
如果您每次都有不同的正则表达式,那么使用String
该类的replaceAll
就可以了。如果您将相同的正则表达式应用于许多字符串,请创建一个Pattern
并重用它。
不变性/线程安全:编译的模式是不可变的,匹配器不是。(请参阅Java 正则表达式线程安全吗?)
处理空字符串:replaceAll 应该优雅地处理空字符串(它不会匹配空输入字符串模式)
煮咖啡等:最后我听说,String、Pattern 和 Matcher 都没有任何 API 功能。
编辑:至于处理 NULL,字符串和模式的文档没有明确说明,但我怀疑他们会抛出 NullPointerException,因为他们期望字符串。
的实现String.replaceAll
告诉你你需要知道的一切:
return Pattern.compile(regex).matcher(this).replaceAll(replacement);
(和文档说同样的话。)
虽然我没有检查缓存,但我当然希望编译一次模式并保持对其的静态引用比Pattern.compile
每次调用相同的模式更有效。如果有缓存,它会节省一点效率 - 如果没有,它可能会很大。
不同之处在于 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。
Matcher::replaceAll
其他答案充分涵盖了 OP 的性能部分,但和之间的另一个区别String::replaceAll
也是编译您自己的Pattern
. 当您Pattern
自己编译时,有诸如标志之类的选项来修改正则表达式的应用方式。例如:
Pattern myPattern = Pattern.compile(myRegex, Pattern.CASE_INSENSITIVE);
将Matcher
应用您在调用时设置的所有标志Matcher::replaceAll
。
您还可以设置其他标志。大多数情况下,我只是想指出Pattern
and Matcher
API 有很多选项,这是超越简单的主要原因String::replaceAll