2015-04-01 20 views
9

Es gibt zwei Methoden:Pattern.split langsamer als 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); 

} 

Und ich sie so im Hauptverfahren testen:

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

Intuitiv, denke ich als String.split schließlich rufen Pattern.compile.split (nach viel mehr Arbeit), um das echte Ding zu tun. Ich kann das Musterobjekt im Voraus konstruieren (es ist threadsicher) und die Aufteilung beschleunigen.

Aber die Tatsache ist, mit dem vorkonstruierten Muster ist viel langsamer als Aufruf String.split direkt. Ich habe eine 50 Zeichen lange Zeichenfolge auf ihnen versucht (mit MyEclipse), der direkte Aufruf verbraucht nur die Hälfte der Zeit der Verwendung vor-konstruierten Musterobjekt.

Bitte kann mir jemand sagen, warum das passiert?

+1

an der Quelle einen Blick. Es ruft 'Pattern.compile (regex) .split (this, limit)' auf, jedoch nur, wenn die zu teilende Zeichenfolge mehr als ein einzelnes Zeichen ist. Zumindest in OpenJDK7, siehe [hier] (http://www.docjar.com/html/api/java/lang/String.java.html), Zeile 2312. –

+0

@tobias_k Lustig, du bist der Einzige, der darauf verweist aus diesem entscheidenden Detail ... in einem Kommentar, wo alle anderen Leute unvollständige Antworten schreiben. – GhostCat

+0

Lose verwandte Antwort - http://stackoverflow.com/a/26159501/2182928 –

Antwort

4

bekommen Dies kann von der tatsächlichen Implementierung von Java ab. Ich verwende OpenJDK 7, und hier ruft String.split in der Tat Pattern.compile(regex).split(this, limit), aber nur auf, wenn die Zeichenfolge zu teilen, regex, mehr als ein einzelnes Zeichen ist.

Siehe here für den Quellcode, Linie 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); 
} 

Wie Sie Spaltung von "\\." sind, den „schnellen Weg“ verwendet. Das heißt, wenn Sie OpenJDK verwenden.

+0

Aber '\\.' Ist eigentlich eine Zwei-String-String, liege ich falsch? – sp00m

+0

@ sp00m Siehe den Kommentar: "(2) Two-Char String und das erste Zeichen ist der Backslash und das zweite ist nicht die ASCII-Ziffer oder ASCII-Zeichen." –

+0

Meine Schuld, du hast Recht, das wurde tatsächlich in den * anderen Checks und vielen Low-Level-Code * implementiert :) – sp00m

0

Ich denke, dies kann nur durch JIT-Optimierung erläutert, String.split intern nicht implementiert ist wie folgt:

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

und es funktioniert schneller, wenn es in String.class ist, aber wenn ich den gleichen Code im Test:

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

ich das gleiche Ergebnis wie p.split(base)

2

Dies ist die Änderung in String.split Verhalten, das in Java 7 gemacht wurde. Dies ist, was wir have in 7u40:

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

Und das ist, was wir had in 6-b14

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