Frage

Ermutigt von dieser , und die Tatsache, ich Milliarden Zeichenfolge haben zu analysieren ich versuchte, meinen Code zu ändern, zu akzeptieren StringTokenizer statt String []

Das einzige, was zwischen mir gelassen und dem köstlichen x2 Leistungsschub bekommt, ist die Tatsache, dass, wenn Sie tun

"dog,,cat".split(",")
//output: ["dog","","cat"]

StringTokenizer("dog,,cat")
// nextToken() = "dog"
// nextToken() = "cat"

Wie kann ich ähnliche Ergebnisse mit den StringTokenizer erreichen? Gibt es schnellere Wege, dies zu tun?

War es hilfreich?

Lösung

Sind Sie eigentlich nur Zeichenüber auf Kommas? Wenn ja, würde ich meine eigenen tokenizer schreiben - es auch noch effizienter als der allgemeineren Zweck kann am Ende StringTokenizer zu sein, die für mehrere Token aussehen können, und Sie können es jedoch machen verhalten Sie mögen. Für einen solchen einfachen Anwendungsfall kann es sich um eine einfache Implementierung sein.

Wenn es sinnvoll sei, könnten Sie sogar Iterable<String> implementieren und erhalten verbesserte-for-Schleife Unterstützung mit starken Typisierung statt der Enumeration Unterstützung durch StringTokenizer zur Verfügung gestellt. Lassen Sie mich wissen, wenn Sie Hilfe wünschen Codierung ein solches Tier auf -. Es sollte wirklich nicht zu hart sein

Darüber hinaus würde ich versuchen, Performance-Tests auf Ihren aktuellen Daten läuft, bevor zu weit von einer bestehenden Lösung heraus springt. Sie haben keine Ahnung, wie viel von Ihrer Ausführungszeit ist wirklich in String.split ausgegeben? Ich weiß, dass Sie eine Menge Saiten zu analysieren, aber wenn Sie etwas bedeutende danach mit ihnen zu tun sind, würde ich erwarten, dass als die Spaltung viel bedeutender sein.

Andere Tipps

Nach Bastelei mit der StringTokenizer Klasse , kann ich nicht einen Weg finden, um die Anforderungen zu erfüllen ["dog", "", "cat"] zurückzukehren.

Darüber hinaus wird die StringTokenizer Klasse links nur aus Kompatibilitätsgründen, und die Verwendung von String.split ist encouaged. Von der API-Spezifikation für die StringTokenizer:

  

StringTokenizer ist ein Vermächtnis Klasse   das ist für die Kompatibilität beibehalten   Gründe obwohl seine Verwendung ist   in neuem Code abgeraten. Es ist   empfohlen, dass jemand diese suchen   Funktionelle split Methode   von String oder java.util.regex   Paket statt.

Da das Problem ist die angeblich schlechte Leistung des String.split Methode, müssen wir eine Alternative finden.

Hinweis: Ich sage „angeblich schlechte Leistung“, weil es schwer ist, zu bestimmen, dass jeder Anwendungsfall in dem StringTokenizer überlegen führen wird sein auf die String.split Methode. Darüber hinaus in vielen Fällen, es sei denn, die tokenization der Saiten sind in der Tat der Engpass der Anwendung durch geeignete Profilierung bestimmt, ich fühle, dass es sich um eine vorzeitige Optimierung zu sein, wenn etwas am Ende wird. Ich würde geneigt sein, Code schreiben zu sagen, dass, bevor Sie sich auf die Optimierung zu verstehen, sinnvoll und einfach ist.

Nun, von den aktuellen Anforderungen, wahrscheinlich unsere eigenen tokenizer rollen wäre nicht allzu schwierig.

Rollen eigene tokenzier!

Das folgende ist ein einfaches tokenizer ich geschrieben habe. Ich sollte anmerken, dass es keine Geschwindigkeitsoptimierungen sind, noch gibt es Fehlerkontrollen vorbei am Ende der Schnur zu verhindern - das ist eine schnelle und unsaubere Implementierung:

class MyTokenizer implements Iterable<String>, Iterator<String> {
  String delim = ",";
  String s;
  int curIndex = 0;
  int nextIndex = 0;
  boolean nextIsLastToken = false;

  public MyTokenizer(String s, String delim) {
    this.s = s;
    this.delim = delim;
  }

  public Iterator<String> iterator() {
    return this;
  }

  public boolean hasNext() {
    nextIndex = s.indexOf(delim, curIndex);

    if (nextIsLastToken)
      return false;

    if (nextIndex == -1)
      nextIsLastToken = true;

    return true;
  }

  public String next() {
    if (nextIndex == -1)
      nextIndex = s.length();

    String token = s.substring(curIndex, nextIndex);
    curIndex = nextIndex + 1;

    return token;
  }

  public void remove() {
    throw new UnsupportedOperationException();
  }
}

Die MyTokenizer wird ein String tokenize und String als Trennzeichen, und verwenden Sie die String.indexOf Methode die Suche nach Trennzeichen durchzuführen. Tokens werden von dem String.substring Verfahren hergestellt wird.

Ich würde vermuten, könnte es einige Leistungsverbesserungen werden durch am char[] Ebene auf die Saite arbeitet und nicht auf der String Ebene. Aber das werde ich den Leser als Übung überlassen.

Die Klasse implementiert auch Iterable und Iterator , um die Vorteile des for-each zu nehmen Schleife Konstrukt, das in Java 5. StringTokenizer eingeführt wurde, ist ein Enumerator, und unterstützt nicht die for-each Konstrukt.

Ist es nicht schneller?

Um herauszufinden, ob dies ist schneller, schrieb ich ein Programm auf Geschwindigkeiten in den folgenden vier Methoden vergleichen:

  1. Die Verwendung von StringTokenizer.
  2. Die Verwendung des neuen MyTokenizer.
  3. Die Verwendung von String.split.
  4. Die Verwendung von vorkompilierten regulären Ausdruck von Pattern.compile .

In den vier Methoden wurde die Zeichenfolge "dog,,cat" in Token getrennt. Obwohl die StringTokenizer im Vergleich enthalten ist, sei darauf hingewiesen, dass es nicht das gewünschte Ergebnis ["dog", "", "cat] zurückkehren wird.

Die Tokenisieren wurden für insgesamt 1 Million Mal wiederholt genug Zeit zu geben, um die Unterschiede in den Methoden zu bemerken.

Der Code für die einfache Benchmark war die folgende:

long st = System.currentTimeMillis();
for (int i = 0; i < 1e6; i++) {
  StringTokenizer t = new StringTokenizer("dog,,cat", ",");
  while (t.hasMoreTokens()) {
    t.nextToken();
  }
}
System.out.println(System.currentTimeMillis() - st);

st = System.currentTimeMillis();
for (int i = 0; i < 1e6; i++) {
  MyTokenizer mt = new MyTokenizer("dog,,cat", ",");
  for (String t : mt) {
  }
}
System.out.println(System.currentTimeMillis() - st);

st = System.currentTimeMillis();
for (int i = 0; i < 1e6; i++) {
  String[] tokens = "dog,,cat".split(",");
  for (String t : tokens) {
  }
}
System.out.println(System.currentTimeMillis() - st);

st = System.currentTimeMillis();
Pattern p = Pattern.compile(",");
for (int i = 0; i < 1e6; i++) {
  String[] tokens = p.split("dog,,cat");
  for (String t : tokens) {
  }
}
System.out.println(System.currentTimeMillis() - st);

Die Ergebnisse

Die Tests waren run Java SE 6 mit (build 1.6.0_12-b04), und die Ergebnisse waren wie folgt vor:

                   Run 1    Run 2    Run 3    Run 4    Run 5
                   -----    -----    -----    -----    -----
StringTokenizer      172      188      187      172      172
MyTokenizer          234      234      235      234      235
String.split        1172     1156     1171     1172     1156
Pattern.compile      906      891      891      907      906

So, wie aus den begrenzten Tests und nur fünf Läufen zu sehen ist, die StringTokenizer in der Tat die schnellsten kommen, aber die MyTokenizer kam als enges 2. in. Dann war String.split die langsamste und der vorkompilierte reguläre Ausdruck war etwas schneller als die split Methode.

Wie bei jedem kleinen Maßstab, ist es wahrscheinlich nicht sehr repräsentativ für Realbedingungen, so sollten die Ergebnisse mit einem Korn genommen werden (oder einem Hügel) Salz.

Hinweis: einige schnelle Benchmarks getan, Scanner stellt sich heraus, etwa viermal langsamer als String.split zu sein. Daher nicht Scanner verwenden.

(Ich verlasse die Post auf die Tatsache zu erfassen, dass Scanner eine schlechte Idee, in diesem Fall ist (lesen als:. Sie downvote mich nicht für Scanner was darauf hindeutet, bitte ...))

Angenommen, Sie Java 1.5 oder höher verwenden, versuchen Sie Scanner , die Iterator<String> implementiert, wie es geschieht:

Scanner sc = new Scanner("dog,,cat");
sc.useDelimiter(",");
while (sc.hasNext()) {
    System.out.println(sc.next());
}

gibt:

dog

cat

Je nachdem, welche Art von Zeichenketten Sie tokenize benötigen, können Sie Ihre eigenen Splitter auf String.indexOf () beispielsweise auf Basis schreiben. Sie könnten auch eine Multi-Core-Lösung, um die Leistung noch weiter zu verbessern, da die tokenization von Strings erstellen voneinander unabhängig ist. Die Arbeiten an Chargen von -Ermöglicht Say-100 Strings pro Kern. Haben die String.split () oder watever anderes.

Anstatt StringTokenizer, könnten Sie die StrTokenizer Klasse von Apache Commons Lang versuchen, die ich zitiere:

  

Diese Klasse kann ein String in viele kleinere Strings aufgeteilt. Ziel ist es, einen ähnlichen Job zu StringTokenizer zu tun, aber es ist viel mehr Kontrolle und Flexibilität bietet, einschließlich dem ListIterator Schnittstelle implementiert.

     

Leere Token können als null entfernt oder zurückgeschickt werden.

Das klingt wie das, was Sie brauchen, denke ich?

Sie könnte so etwas tun. Es ist nicht perfekt, aber es könnte für Sie arbeiten.

public static List<String> find(String test, char c) {
    List<String> list = new Vector<String>();
    start;
    int i=0;
    while (i<=test.length()) {
        int start = i;
        while (i<test.length() && test.charAt(i)!=c) {
            i++;
        }
        list.add(test.substring(start, i));
        i++;
    }
    return list;
}

Wenn möglich, können Sie die Liste, was ommit und direkt etwas tun, um die Teilkette:

public static void split(String test, char c) {
    int i=0;
    while (i<=test.length()) {
        int start = i;
        while (i<test.length() && test.charAt(i)!=c) {
            i++;
        }
        String s = test.substring(start,i);
         // do something with the string here
        i++;
    }
}

Auf meinem System die letzte Methode ist schneller als die StringTokenizer-Lösung, aber Sie könnten testen wollen, wie es für Sie arbeitet. (Natürlich könnte man diese Methode macht ein wenig kürzer ommiting die {} der zweiten, während Aussehen und natürlich könnten Sie ein verwenden for-Schleife anstelle der äußeren while-Schleife einschließlich dem letzten i ++ in dem, aber ich didn‘ t tun, dass hier, weil ich so schlecht Stil betrachten.

Nun, das schnellste, was man tun könnte, wäre, manuell die Zeichenfolge zu durchqueren, z.B.

List<String> split(String s) {
        List<String> out= new ArrayList<String>();
           int idx = 0;
           int next = 0;
        while ( (next = s.indexOf( ',', idx )) > -1 ) {
            out.add( s.substring( idx, next ) );
            idx = next + 1;
        }
        if ( idx < s.length() ) {
            out.add( s.substring( idx ) );
        }
               return out;
    }

Der (informelle Test) sieht aus, als Split etwas wie doppelt so schnell zu sein. Allerdings ist es ein bisschen gefährlich, so zu wiederholen, zum Beispiel auf entkam Komma wird es brechen, und wenn Sie am Ende brauchen mit, dass an einem gewissen Punkt zu behandeln (weil Ihre Liste von einer Milliarde Strings hat 3 entkam Komma) durch die Zeit, die Sie Hat es erlauben Sie werden wahrscheinlich einige des Geschwindigkeitsvorteils am Ende verlieren.

Schließlich ist es wahrscheinlich nicht wert, die stören.

Ich würde empfehlen, Google Guava Splitter.
Ich verglich es mit coobird Test und bekam folgende Ergebnisse:

  

StringTokenizer 104
  Google Guava Splitter 142
  String.split 446
  regexp 299

Wenn Sie Ihre Eingabe strukturiert ist, können Sie einen Blick auf die JavaCC Compiler. Er erzeugt eine Java-Klasse das Lesen Sie Ihre Eingabe. Es würde wie folgt aussehen:

TOKEN { <CAT: "cat"> , <DOG:"gog"> }

input: (cat() | dog())*


cat: <CAT>
   {
   animals.add(new Animal("Cat"));
   }

dog: <DOG>
   {
   animals.add(new Animal("Dog"));
   }
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top