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

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

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

【问题讨论】:

    标签: java regex string replaceall


    【解决方案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 的主要原因

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2012-06-10
              • 2012-06-05
              • 2011-07-08
              • 2018-07-10
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多