2015-04-01 19 views
9

ci sono due metodi:Pattern.split più lento di 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); 

} 

E io testarli come questo nel metodo principale:

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

Intuitivamente, penso che come String.split finiranno per chiamare Pattern.compile.split (dopo un sacco di lavoro extra) per fare la cosa reale. Posso costruire l'oggetto Pattern in anticipo (è thread-safe) e accelerare la divisione.

Ma il fatto è che l'utilizzo del modello precostruito è molto più lento rispetto al numero String.split direttamente. Ho provato una stringa lunga 50 caratteri (usando MyEclipse), la chiamata diretta consuma solo metà del tempo di utilizzo di un oggetto Pattern pre-costruito.

Per favore qualcuno può dirmi perché questo succede?

+1

dare un'occhiata alla fonte. Si chiama _ Pattern.compile (regex) .split (this, limit) ', ma solo se la stringa da dividere è più di un singolo carattere. Almeno in OpenJDK7, vedi [qui] (http://www.docjar.com/html/api/java/lang/String.java.html), riga 2312. –

+0

@tobias_k Divertente, sei l'unico a puntare questo dettaglio cruciale ... in un commento, dove tutte le altre persone scrivono invece risposte incomplete. – GhostCat

+0

Risposta approssimativa - http://stackoverflow.com/a/26159501/2182928 –

risposta

4

Questo può dipendere l'effettiva attuazione di Java. Sto usando OpenJDK 7, e qui, String.split effettivamente invoca Pattern.compile(regex).split(this, limit), ma solo se la stringa da dividere, regex, è più di un singolo carattere.

Vedere here per il codice sorgente, la linea 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); 
} 

Come si stanno dividendo da "\\." che usa il "percorso veloce". Cioè, se stai usando OpenJDK.

+0

Ma '\\.' È in realtà una stringa di due caratteri, ho torto? – sp00m

+0

@ sp00m Vedere il commento: "(2) stringa di due caratteri e il primo carattere è il backslash e il secondo non è la lettera ascii o la lettera ascii." –

+0

La mia colpa, hai ragione, è stata effettivamente implementata nel * mazzo di altri assegni e un sacco di codice di basso livello * :) – sp00m

0

Penso che questo può essere spiegato solo con l'ottimizzazione JIT, String.split internamente non è implementato come segue:

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

e funziona più velocemente quando è all'interno String.class, ma quando uso la stessa il codice nel 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); 
    } 

sto ottenendo lo stesso risultato di p.split(base)

2

Questa è la modifica nel comportamento String.split, che è stata effettuata in Java 7. Questo è ciò che 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); 
} 

E questo è quello che ci had in 6-b14

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