2015-04-01 61 views
9

有两种方法:Pattern.split慢于String.split

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对象的一半时间。

请有人能告诉我为什么发生这种情况?

+1

看看源。它调用'Pattern.compile(regex).split(this,limit)',但前提是要分割的字符串不止一个字符。至少在OpenJDK7中,参见[这里](http://www.docjar.com/html/api/java/lang/String.java.html),第2312行。 – 2015-04-01 14:21:20

+0

@tobias_k有趣的是,你是唯一一个指向在关键的细节中......所有其他人都写下了不完整的答案。 – GhostCat 2015-04-01 14:32:27

+0

松散相关的答案 - http://stackoverflow.com/a/26159501/2182928 – 2015-04-01 14:46:31

回答

4

这可能取决于实际的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。

+0

但是'\\。'实际上是一个双字符串,我错了吗? – sp00m 2015-04-01 14:45:58

+0

@ sp00m请参阅注释:“(2)two-char字符串,第一个字符是反斜杠,第二个字符不是ascii数字或ascii字母。“ – 2015-04-01 14:46:58

+0

我错了,你是对的,这实际上是在其他一些检查和许多底层代码中实现的。 – sp00m 2015-04-01 14:48:52

0

我认为这只能通过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)

2

这是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); 
}