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
和显式创建Matcher
和Pattern
之间的性能应该相同。
编辑
正如 cmets 中所指出的,从 String
或 Matcher
对 replaceAll
的单个调用将不存在性能差异,但是,如果需要对 @ 执行多个调用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】:主要区别在于,如果您保留用于生成Matcher
的Pattern
,则可以避免每次使用正则表达式时都重新编译它。通过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::replaceAll
和 String::replaceAll
之间的另一个区别也是编译您自己的 Pattern
的原因。当您自己编译 Pattern
时,可以使用诸如标志之类的选项来修改正则表达式的应用方式。例如:
Pattern myPattern = Pattern.compile(myRegex, Pattern.CASE_INSENSITIVE);
Matcher
将应用您在调用 Matcher::replaceAll
时设置的所有标志。
您还可以设置其他标志。大多数情况下,我只是想指出Pattern
和Matcher
API 有很多选项,这是超越简单String::replaceAll
的主要原因
【讨论】:
以上是关于String replaceAll() 与 Matcher replaceAll()(性能差异)的主要内容,如果未能解决你的问题,请参考以下文章
String replace() 和 replaceAll() 的区别