W3docs

Java StringTokenizer

Strings in Java mit dem veralteten StringTokenizer aufteilen und wann stattdessen String.split verwendet werden sollte.

java.util.StringTokenizer ist die ursprüngliche JDK-Klasse zum „Aufteilen eines Strings in Teile" — ein zeichenweiser Tokenizer, der seit Version 1.0 zur Plattform gehört. Das Javadoc selbst rät davon für neuen Code ab: StringTokenizer ist eine Legacy-Klasse, die aus Kompatibilitätsgründen beibehalten wird, obwohl ihre Verwendung in neuem Code nicht empfohlen wird. Es wird empfohlen, dass alle, die diese Funktionalität suchen, stattdessen die Methode split von String oder das Paket java.util.regex verwenden."

Das ist die Kernaussage, und sie ist korrekt — StringTokenizer taucht jedoch weiterhin in echten Codebasen auf und hat einige legitime Nischen. Dieses Kapitel behandelt ihn kurz und zeigt klar, wann stattdessen String.split oder Scanner zu verwenden sind.

Die grundlegende Schleife

Ein StringTokenizer ist eine Enumeration von Teilstrings:

StringTokenizer st = new StringTokenizer("apple banana cherry");
while (st.hasMoreTokens()) {
  System.out.println(st.nextToken());
}
// apple
// banana
// cherry

Die Standardmenge der Trennzeichen ist Leerzeichen — Leerzeichen, Tabulator, Zeilenumbruch, Wagenrücklauf, Seitenvorschub. Aufeinanderfolgende Trennzeichen werden zusammengefasst: " a b " ergibt genau zwei Token.

Benutzerdefinierte Trennzeichen

Der zweite Konstruktor nimmt einen String, dessen Zeichen jeweils als Trennzeichen behandelt werden — nicht als Trennzeichenfolge:

StringTokenizer st = new StringTokenizer("one,two;three|four", ",;|");
while (st.hasMoreTokens()) {
  System.out.println(st.nextToken());
}
// one
// two
// three
// four

Das ist die Besonderheit im Design, die viele Menschen verwirrt. new StringTokenizer(input, ", ") behandelt , und Leerzeichen jeweils als Trennzeichen, nicht die zweizeichige Folge ", ". Wenn Sie mehrteilige Trennzeichen benötigen, ist StringTokenizer nicht mehr ausreichend.

Die Trennzeichen selbst zurückgeben

Der dreiargumentige Konstruktor steuert, ob Trennzeichen selbst als Token ausgegeben werden:

StringTokenizer st = new StringTokenizer("a+b*c", "+*", true);
while (st.hasMoreTokens()) {
  System.out.println(st.nextToken());
}
// a
// +
// b
// *
// c

Das ist das eine Feature, das String.split nicht direkt nachbildet: Dafür würde man einen Matcher und einen regulären Ausdruck benötigen. Für sehr einfaches Ausdrucks-Parsing — die Art von Aufgabe, die keinen Lexer rechtfertigt — ist diese Überladung noch nützlich.

Token im Voraus zählen

countTokens() gibt an, wie viele Token noch verbleiben (d.h. durch wiederholte nextToken()-Aufrufe erzeugt würden). Es verbraucht sie nicht.

StringTokenizer st = new StringTokenizer("a b c d");
int n = st.countTokens();        // 4
while (st.hasMoreTokens()) st.nextToken();
n = st.countTokens();            // 0

Das ist gelegentlich nützlich, wenn ein Ausgabe-Array der richtigen Größe allokiert werden soll — obwohl man mit String.split einfach .split(...).length aufrufen würde.

Das Trennzeichen zur Laufzeit ändern

Ein weniger bekanntes Feature: nextToken(String newDelims) setzt die Trennzeichenmenge für diesen Aufruf und alle folgenden neu. Nach der Änderung gilt die neue Menge für nachfolgende hasMoreTokens()/nextToken()-Aufrufe, bis Sie sie erneut ändern.

StringTokenizer st = new StringTokenizer("key1=value1; key2=value2; key3=value3");
while (st.hasMoreTokens()) {
  String pair = st.nextToken("; ").trim();  // tokens separated by ';' or space
  System.out.println(pair);
}

Für ad-hoc-Einmal-Parsing kann das praktisch sein. Für wartbaren Code ist es verwirrend — Leser erwarten nicht, dass sich eine Trennzeichenmenge innerhalb einer Schleife ändert.

Warum String.split in der Regel besser ist

Die Gründe, warum String.split (oder Pattern.compile(...).split) für neuen Code bevorzugt werden sollte:

  • Echte Regex-Trennzeichen. Mehrteilige Trennzeichen, Zeichenklassen, Alternation — alles natürlich möglich. StringTokenizer verarbeitet nur einzelne Zeichen als Trennzeichen.
  • Leere Token sind sichtbar. "a,,b".split(",") gibt ["a", "", "b"] zurück. StringTokenizer überspringt das leere Token stillschweigend. Bei CSV-ähnlicher Eingabe ist „das zweite Feld war leer" eine Information, die man in der Regel benötigt.
  • Gibt ein Array zurück. Einfach zu indizieren, einfach in eine List umzuwandeln, einfach zu streamen.
  • Im Allgemeinen schneller unter JIT dank Pattern-Caching für einfache Split-Muster.
  • Einfacher zu testen, einfacher zu lesen. Eine Tokenizer-Schleife liest sich wie 1996; parts = csv.split(",") beschreibt die Absicht klar.

Wann man ihn trotzdem verwenden könnte

Eine kurze Liste von Fällen, in denen StringTokenizer noch vertretbar ist:

  • Streaming über einen sehr langen String, bei dem Token für Token verarbeitet werden soll, ohne ein Array aller Token im Speicher zu halten. StringTokenizer allokiert das Ergebnis nicht im Voraus; split schon.
  • Trennzeichen als Token zurückgeben für den einfachstmöglichen Tokenizer, ohne Pattern/Matcher hinzuzuziehen.
  • Alten Code warten, in dem der Rest der Datei ihn verwendet und Konsistenz das Freundlichste für den nächsten Leser ist.

Für alles andere: Verwenden Sie split.

Ein ausgearbeitetes Beispiel

Das folgende Programm tokenisiert drei Eingaben auf drei verschiedene Arten, jeweils nebeneinander mit den äquivalenten split-Aufrufen, damit die Verhaltensunterschiede sichtbar sind:

java— editable, runs on the server

Zwei Erkenntnisse aus der Ausgabe. In Fall 1 gibt split das leere Feld zwischen green und blue aus ([red, green, , blue]); der Tokenizer fasst es zusammen ([red, green, blue]). In Fall 2 erzeugen beide zufällig dieselben Token, aber aus unterschiedlichen Gründen: Der Tokenizer teilt mix an jedem ; und jedem Leerzeichen unabhängig voneinander auf ("; " bedeutet „jedes dieser Zeichen ist ein Trennzeichen"), während split("; ") die wörtliche zweizeichige Folge "; " abgleicht. Sie stimmen hier nur überein, weil die Trennzeichen in mix genau ; sind. Ändert man die Eingabe auf "k1=v1 ;k2=v2", weichen beide sofort voneinander ab. Welches Verhalten auch immer gewünscht wird — der Code sollte es explizit machen, und das ist mit split viel einfacher.

Was kommt als Nächstes

Bisher haben wir Strings in Teile aufgeteilt. Oft ist es das eigentliche Ziel, einen String in eine Zahl, einen boolean oder ein anderes Primitiv umzuwandeln — und umgekehrt, Primitive zurück in Strings. Dieser Hin- und Rückweg hat seine eigenen Hilfsmethoden und Fallstricke. Weiter geht es mit Java-String-Konvertierungen.

Übungen

Übung
Welches der folgenden ist ein **echter Verhaltensunterschied** zwischen `StringTokenizer` und `String.split`?
Welches der folgenden ist ein **echter Verhaltensunterschied** zwischen `StringTokenizer` und `String.split`?
Was this page helpful?