【问题标题】:Pattern.split slower than String.splitPattern.split 比 String.split 慢
【发布时间】:2015-06-06 05:39:06
【问题描述】:

有两种方法:

private static void normalSplit(String base){
    base.split("\\.");
}

private static final Pattern p = Pattern.compile("\\.");

private static void patternSplit(String base){
    //use the static field above
    p.split(base);

}

我在 main 方法中这样测试它们:

public static void main(String[] args) throws Exception{
    long start = System.currentTimeMillis();
    String longstr = "a.b.c.d.e.f.g.h.i.j";//use any long string you like
    for(int i=0;i<300000;i++){
        normalSplit(longstr);//switch to patternSplit to see the difference
    }
    System.out.println((System.currentTimeMillis()-start)/1000.0);
}

直觉上,我认为String.split 最终会调用Pattern.compile.split(经过大量额外工作)来做真实的事情。我可以提前构造 Pattern 对象(它是线程安全的)并加快拆分速度。

但事实是,使用预先构造的模式比直接调用String.split 慢得多。我在它们上尝试了一个 50 个字符长的字符串(使用 MyEclipse),直接调用只消耗使用预先构造的 Pattern 对象的一半时间。

请有人告诉我为什么会这样?

【问题讨论】:

  • 看看源码。它确实调用Pattern.compile(regex).split(this, limit),但前提是要分割的字符串不止一个字符。至少在 OpenJDK7 中,参见here,第 2312 行。
  • @tobias_k 有趣的是,你是唯一一个指出关键细节的人......在评论中,所有其他人都写了不完整的答案。
  • 松散相关的答案 - stackoverflow.com/a/26159501/2182928

标签: java regex string split


【解决方案1】:

这是String.split 行为的更改,这是在Java 7 中进行的。这就是我们have7u40

public String[] split(String regex, int limit) {
    /* fastpath if the regex is a
     (1)one-char String and this character is not one of the
        RegEx's meta characters ".$|()[{^?*+\\", or
     (2)two-char String and the first char is the backslash and
        the second is not the ascii digit or ascii letter.
     */
    char ch = 0;
    if (((regex.value.length == 1 &&
         ".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) ||
         (regex.length() == 2 &&
          regex.charAt(0) == '\\' &&
          (((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&
          ((ch-'a')|('z'-ch)) < 0 &&
          ((ch-'A')|('Z'-ch)) < 0)) &&
        (ch < Character.MIN_HIGH_SURROGATE ||
         ch > Character.MAX_LOW_SURROGATE))
    {
        //do stuff
        return list.subList(0, resultSize).toArray(result);
    }
    return Pattern.compile(regex).split(this, limit);
}

这就是我们had6-b14中的内容

public String[] split(String regex, int limit) {
    return Pattern.compile(regex).split(this, limit);
}

【讨论】:

    【解决方案2】:

    这可能取决于 Java 的实际实现。我使用的是 OpenJDK 7,在这里,String.split 确实调用了Pattern.compile(regex).split(this, limit),但只有要分割的字符串regex 不止一个字符。

    请参阅here 获取源代码,第 2312 行。

    public String[] split(String regex, int limit) {
       /* fastpath if the regex is a
          (1)one-char String and this character is not one of the
             RegEx's meta characters ".$|()[{^?*+\\", or
          (2)two-char String and the first char is the backslash and
             the second is not the ascii digit or ascii letter.
       */
       char ch = 0;
       if (((regex.count == 1 &&
           // a bunch of other checks and lots of low-level code
           return list.subList(0, resultSize).toArray(result);
       }
       return Pattern.compile(regex).split(this, limit);
    }
    

    当您被"\\." 拆分时,它使用的是“快速路径”。也就是说,如果你使用的是OpenJDK。

    【讨论】:

    • 但是\\.实际上是一个双字符的字符串,我错了吗?
    • @sp00m 查看评论:“(2)two-char 字符串,第一个字符是反斜杠,第二个不是 ascii 数字或 ascii 字母。”
    • 我的错,你是对的,这实际上是在一堆其他检查和许多低级代码中实现的 :)
    【解决方案3】:

    我觉得这只能用JIT优化来解释,String.split内部确实是这样实现的:

    Pattern.compile(regex).split(this, limit);
    

    当它在 String.class 中时它工作得更快,但是当我在测试中使用相同的代码时:

        for (int i = 0; i < 300000; i++) {
            //base.split("\\.");// switch to patternSplit to see the difference
            //p.split(base);
            Pattern.compile("\\.").split(base, 0);
        }
    

    我得到与p.split(base)相同的结果

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-10-11
      • 2021-04-12
      • 2011-08-23
      • 2015-08-03
      • 1970-01-01
      • 2011-08-27
      • 2011-01-28
      相关资源
      最近更新 更多