2013-07-11 2 views
2

Я знаю, что существует много потоков в этом названии. У меня есть код для генерации ngrams. Но хотелось бы знать, может ли он быть улучшен для лучшей скорости при обработке тысяч строк?Способ улучшения поколения ngram?

Пример String = "abcdefghijkl1245ty789"

public static String[] ngrams(String s) { 
     int len=12; 
     String[] parts = s.split("(?!^)"); 
     String[] result = new String[parts.length - len + 1]; 
     for(int i = 0; i < parts.length - len + 1; i++) { 
      StringBuilder sb = new StringBuilder(); 
      for(int k = 0; k < len; k++) { 
       sb.append(parts[i+k]); 
      } 
      result[i] = sb.toString(); 
     } 
     return result; 
    } 

Приведенный выше код получает строку, генерирует ngrmas заданной длины. В моем случае его 12.

+0

Если у вас есть огромное количество входных строк с достаточной вероятностью повторения входных строк, тогда вы можете захотеть рассмотреть мемуацию этого метода, так как вывод зависит только от ввода. – Dancrumb

ответ

6

Sure:

public static String[] ngrams(String str, int length) { 
    char[] chars = str.toCharArray(); 
    final int resultCount = chars.length - length + 1; 
    String[] result = new String[resultCount]; 
    for (int i = 0; i < resultCount; i++) { 
     result[i] = new String(chars, i, length); 
    } 
    return result; 
} 

Изменения, которые я сделал:

  • вместо расщепления с помощью регулярного выражения, я использовал String#toCharArray(), который делает одну копию массива и поэтому много быстрее
  • вместо того, чтобы перестроить полученные строки из StringBuilder, я использовал an appropriate String constructor, который, опять же, делает только один arraycopy
  • (не требуется для производительности, но все же) Я изменил подпись метода на length в качестве параметра для моих целей тестирования. Не стесняйтесь менять его обратно - просто убедитесь, что вы переименовали метод от ngrams() до ngrams12() или что-то в этом роде.

Или бросьте все, в целом, и использовать наивное подход с String#substring(), что делает подобную работу под капотом:

public static String[] ngramsSubstring(String str, int length) { 
    final int resultCount = str.length() - length + 1; 
    String[] result = new String[resultCount]; 
    for (int i = 0; i < resultCount; i++) { 
     result[i] = str.substring(i, i+length); 
    } 
    return result; 
} 

Кстати, если вы когда-либо приходилось использовать regexp в будущем, попробуйте выполнить его компиляцию один раз и повторно использовать его, а не компилировать его каждый раз, когда метод будет использоваться. Например, ваш код будет выглядеть следующим образом:

private static final Pattern EVERY_CHAR = Pattern.compile("(?!^)"); 

, а затем, в способе, вместо String#split, вы бы использовать

String[] parts = EVERY_CHAR.split(str); 
+0

Не стесняйтесь задавать любые вопросы! –

+0

Спасибо за ответ. Отправляя параметр подстроки, я читаю в каком-то обсуждении, что использование подстроки вызывает создание нового строкового объекта каждый раз? так что обычно это вызывает ошибку пространства кучи при обработке множества подстрочных операций для генерации ngram? – Balaram26

+0

@ Balaram26 Я не уверен, что правильно понимаю. Ваше решение, так же как и мое, создает новый экземпляр String каждый раз, когда они назначают 'result [i]'. Кроме того, ваше решение создаст объект 'StringBuilder'. Также имеется некоторое 'char []' копирование. До обновления 7 Java 7 подстрока() 'разделяла исходный массив символов и поэтому сохраняла бы много памяти (и' char [] 'распределения и копии). –

Смежные вопросы