2015-04-01 5 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); 

} 

И я протестировать их, как это основной метод:

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

Посмотрите на источник. Это _does_ вызывает 'Pattern.compile (regex) .split (this, limit)', но только если строка для разделения более одного символа. По крайней мере, в OpenJDK7 см. [Здесь] (http://www.docjar.com/html/api/java/lang/String.java.html), строка 2312. –

+0

@tobias_k Забавно, вы единственное, из этой важной детали ... в комментарии, где все остальные люди пишут неполные ответы вместо этого. – GhostCat

+0

Непосредственный ответ - http://stackoverflow.com/a/26159501/2182928 –

ответ

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

+0

@ sp00m Посмотреть комментарий: "(2) строка с двумя символами и первый символ - обратная косая черта, а вторая - не цифра ascii или ascii.« –

+0

Моя вина, вы правы, это было фактически реализовано в * связке других проверок и много низкоуровневого кода * :) – sp00m

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. Это то, что мы have в 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); 
} 

И это то, что мы had в 6-b14

public String[] split(String regex, int limit) { 
    return Pattern.compile(regex).split(this, limit); 
} 
Смежные вопросы