performance der StringTokenizer-class im Vergleich zur String.split-Methode in Java

In meiner Software muss ich String in Wörter aufteilen. Ich habe derzeit mehr als 19.000.000 Dokumente mit jeweils mehr als 30 Wörtern.

Welcher der folgenden zwei Wege ist der beste Weg dies zu tun (in Bezug auf die performance)?

StringTokenizer sTokenize = new StringTokenizer(s," "); while (sTokenize.hasMoreTokens()) { 

oder

 String[] splitS = s.split(" "); for(int i =0; i < splitS.length; i++) 

   

Wenn Ihre Daten bereits in einer database vorhanden sind, müssen Sie die Wortkette analysieren. Ich würde vorschlagen, indexOf wiederholt zu verwenden. Es ist viel schneller als jede Lösung.

Es ist jedoch immer noch wahrscheinlich, dass die Daten aus einer database viel teurer werden.

 StringBuilder sb = new StringBuilder(); for (int i = 100000; i < 100000 + 60; i++) sb.append(i).append(' '); String sample = sb.toString(); int runs = 100000; for (int i = 0; i < 5; i++) { { long start = System.nanoTime(); for (int r = 0; r < runs; r++) { StringTokenizer st = new StringTokenizer(sample); List list = new ArrayList(); while (st.hasMoreTokens()) list.add(st.nextToken()); } long time = System.nanoTime() - start; System.out.printf("StringTokenizer took an average of %.1f us%n", time / runs / 1000.0); } { long start = System.nanoTime(); Pattern spacePattern = Pattern.compile(" "); for (int r = 0; r < runs; r++) { List list = Arrays.asList(spacePattern.split(sample, 0)); } long time = System.nanoTime() - start; System.out.printf("Pattern.split took an average of %.1f us%n", time / runs / 1000.0); } { long start = System.nanoTime(); for (int r = 0; r < runs; r++) { List list = new ArrayList(); int pos = 0, end; while ((end = sample.indexOf(' ', pos)) >= 0) { list.add(sample.substring(pos, end)); pos = end + 1; } } long time = System.nanoTime() - start; System.out.printf("indexOf loop took an average of %.1f us%n", time / runs / 1000.0); } } 

Drucke

 StringTokenizer took an average of 5.8 us Pattern.split took an average of 4.8 us indexOf loop took an average of 1.8 us StringTokenizer took an average of 4.9 us Pattern.split took an average of 3.7 us indexOf loop took an average of 1.7 us StringTokenizer took an average of 5.2 us Pattern.split took an average of 3.9 us indexOf loop took an average of 1.8 us StringTokenizer took an average of 5.1 us Pattern.split took an average of 4.1 us indexOf loop took an average of 1.6 us StringTokenizer took an average of 5.0 us Pattern.split took an average of 3.8 us indexOf loop took an average of 1.6 us 

Die Kosten für das Öffnen einer Datei betragen ca. 8 ms. Da die Dateien so klein sind, kann der Cache die performance um den Faktor 2-5x verbessern. Trotzdem wird es ~ 10 Stunden dauern, Dateien zu öffnen. Die Kosten für die Verwendung von Split vs StringTokenizer betragen jeweils weit weniger als 0,01 ms. Um 19 Millionen x 30 Wörter zu parsen * sollten 8 Buchstaben pro Wort etwa 10 Sekunden dauern (bei etwa 1 GB pro 2 Sekunden)

Wenn Sie die performance verbessern möchten, schlage ich vor, dass Sie weit weniger Dateien haben. zB benutze eine database. Wenn Sie keine SQL-database verwenden möchten, empfehle ich eine dieser http://nosql-database.org/

Split in Java 7 ruft nur indexOf für diese Eingabe auf, siehe Quelle . Split sollte sehr schnell sein, in der Nähe von wiederholten Aufrufen von indexOf.

Die Java-API-Spezifikation empfiehlt die Verwendung von split . Siehe die Dokumentation von StringTokenizer .

Eine weitere wichtige Sache, undokumentiert, soweit ich es bemerkt habe, besteht darin, dass der StringTokenizer die Begrenzer zusammen mit der Token-Zeichenfolge StringTokenizer(String str, String delim, boolean returnDelims) durch Verwendung des Konstruktors StringTokenizer(String str, String delim, boolean returnDelims) ) reduziert sich auch die Verarbeitungszeit. Also, wenn Sie nach performance suchen, würde ich empfehlen, etwas wie zu verwenden:

 private static final String DELIM = "#"; public void splitIt(String input) { StringTokenizer st = new StringTokenizer(input, DELIM, true); while (st.hasMoreTokens()) { String next = getNext(st); System.out.println(next); } } private String getNext(StringTokenizer st){ String value = st.nextToken(); if (DELIM.equals(value)) value = null; else if (st.hasMoreTokens()) st.nextToken(); return value; } 

Trotz des durch die getNext () -Methode eingeführten Overheads, der die Begrenzer für Sie verwirft, ist er nach meinen Benchmarks immer noch 50% schneller.

Verwenden Sie teilen.

StringTokenizer ist eine Legacy-class, die aus Kompatibilitätsgründen beibehalten wird, obwohl deren Verwendung in neuem Code nicht empfohlen wird. Es wird empfohlen, dass jeder, der diese functionalität sucht, stattdessen die Split-Methode verwendet.

Was müssen die 19.000.000 Dokumente dort tun? Müssen Sie in allen Dokumenten regelmäßig Wörter aufteilen? Oder ist es ein One-Shoot-Problem?

Wenn Sie ein Dokument mit nur 30 Wörtern anzeigen / anfordern, ist dies ein so kleines Problem, dass jede Methode funktionieren würde.

Wenn Sie alle Dokumente mit nur 30 Wörtern gleichzeitig bearbeiten müssen, ist dies ein so kleines Problem, dass Sie wahrscheinlich ohnehin IO-gebunden sind.

Bei der Ausführung von Micro-Benchmarks (und in diesem Fall sogar Nano-Benchmarks) wirkt sich vieles auf Ihre Ergebnisse aus. JIT-Optimierungen und Garbage Collection, um nur einige zu nennen.

Um aussagekräftige Ergebnisse aus den Mikrobenchmarks zu erhalten, sehen Sie sich die jmh- Bibliothek an. Es verfügt über exzellente Beispiele für gute Benchmarks.

Unabhängig von seinem Legacy-Status würde ich StringTokenizer für diese Aufgabe bedeutend schneller als String.split() erwarten, da es keine regulären Ausdrücke verwendet: Es scannt nur die Eingabe direkt, ähnlich wie Sie selbst über indexOf() . Tatsächlich muss String.split() den Regex jedes Mal kompilieren, wenn Sie ihn aufrufen, also ist es nicht einmal so effizient wie ein regulärer Ausdruck direkt selbst.

Dies könnte ein sinnvolles Benchmarking mit 1.6.0 sein

 http://www.javamex.com/tutorials/regular_expressions/splitting_tokenisation_performance.shtml#.V6-CZvnhCM8 

Performance-weise StringTokeniser ist viel besser als Split. Überprüfen Sie den Code unten,

Bildbeschreibung hier eingeben

Aber laut Java-Dokumenten wird davon abgeraten. Überprüfen Sie hier