W3docs

Java StringTokenizer

Divida strings em tokens com a classe legada StringTokenizer e saiba quando preferir String.split no Java.

java.util.StringTokenizer é a classe original do JDK para "dividir uma string em partes" — um tokenizador caractere a caractere que existe na plataforma desde a versão 1.0. O próprio Javadoc recomenda não utilizá-la em código novo: "StringTokenizer é uma classe legada mantida por razões de compatibilidade, embora seu uso seja desencorajado em código novo. Recomenda-se que qualquer pessoa que busque essa funcionalidade utilize o método split de String ou o pacote java.util.regex."

Esse é o ponto central, e é correto — mas StringTokenizer ainda aparece em bases de código reais e possui algumas situações em que ainda faz sentido. Este capítulo a ensina brevemente e então indica claramente quando recorrer a String.split ou Scanner.

O laço básico

Um StringTokenizer é uma Enumeration de substrings:

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

O conjunto de delimitadores padrão é espaço em branco — espaço, tabulação, nova linha, retorno de carro, avanço de formulário. Delimitadores adjacentes são colapsados: " a b " produz exatamente dois tokens.

Delimitadores personalizados

O segundo construtor recebe uma string cujos caracteres são cada um tratado como delimitador — não como uma string delimitadora:

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

Essa é a peculiaridade de design que confunde as pessoas. new StringTokenizer(input, ", ") trata , e espaço cada um como delimitador, não a sequência de dois caracteres ", ". Se você precisar de delimitadores com múltiplos caracteres, StringTokenizer já não é suficiente.

Retornando os próprios delimitadores

O construtor com três argumentos controla se os delimitadores são emitidos como tokens:

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

Esse é o único recurso que String.split não replica diretamente: seria necessário usar um Matcher com uma regex. Para análise de expressões muito simples — do tipo que não justifica trazer um lexer — essa sobrecarga ainda justifica seu uso.

Contando tokens antecipadamente

countTokens() informa quantos tokens restam (ou seja, quantos seriam produzidos por chamadas sucessivas de nextToken()). Ele não os consome.

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

Isso pode ser útil ao alocar um array de saída com o tamanho correto — embora com String.split bastasse chamar .split(...).length.

Alterando o delimitador durante a iteração

Um recurso menos conhecido: nextToken(String newDelims) redefine o conjunto de delimitadores a partir dessa chamada em diante. Uma vez alterado, o novo conjunto persiste para chamadas subsequentes de hasMoreTokens()/nextToken() até ser alterado novamente.

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);
}

Para análises pontuais e informais, isso pode ser elegante. Para qualquer coisa que precise ser mantida, é confuso — leitores não esperam que um conjunto de delimitadores mude dentro de um laço.

Por que String.split costuma ser melhor

Os motivos para preferir String.split (ou Pattern.compile(...).split) em código novo:

  • Delimitadores reais com regex. Delimitadores com múltiplos caracteres, classes de caracteres, alternância — tudo natural. StringTokenizer só trata delimitadores de um único caractere.
  • Tokens vazios são visíveis. "a,,b".split(",") retorna ["a", "", "b"]. StringTokenizer ignora o token vazio silenciosamente. Para entradas no formato CSV, "o segundo campo estava vazio" é uma informação que geralmente você precisa.
  • Retorna um array. Fácil de indexar, fácil de converter para List, fácil de processar com streams.
  • Geralmente mais rápido sob JIT graças ao cache de Pattern para padrões de divisão simples.
  • Mais fácil de testar e de ler. Um laço de tokenizador parece código de 1996; parts = csv.split(",") expressa a intenção claramente.

Quando ainda pode valer a pena usá-lo

Uma lista curta de casos em que StringTokenizer ainda é defensável:

  • Iteração sobre uma string muito longa em que você deseja consumir token por token sem manter um array com todos eles. StringTokenizer não aloca o resultado antecipadamente; split sim.
  • Retornar delimitadores como tokens para o tokenizador mais simples possível, sem recorrer a Pattern/Matcher.
  • Manutenção de código antigo onde o restante do arquivo o utiliza e a consistência é o mais gentil para o próximo leitor.

Para todo o resto: use split.

Um exemplo completo

O programa abaixo tokeniza três entradas de três formas diferentes, lado a lado com as chamadas equivalentes a split, para que as diferenças comportamentais fiquem visíveis:

java— editable, runs on the server

Duas conclusões a partir da saída. No caso 1, split reporta a célula vazia entre green e blue ([red, green, , blue]); o tokenizador a colapsa ([red, green, blue]). No caso 2, ambos produzem os mesmos tokens, mas por razões diferentes: o tokenizador divide mix a cada ; e a cada espaço de forma independente ("; " significa "qualquer um desses caracteres é delimitador"), enquanto split("; ") corresponde à sequência literal de dois caracteres "; ". Eles concordam aqui apenas porque os separadores em mix são exatamente ; . Mude a entrada para "k1=v1 ;k2=v2" e os dois divergem imediatamente. Qualquer que seja o comportamento desejado, o código deve ser explícito sobre isso — e isso é muito mais fácil com split.

O que vem a seguir

Até agora dividimos strings em partes. Muitas vezes o que você realmente precisa é converter uma string em um número, um boolean ou outro tipo primitivo — e no sentido inverso, primitivos de volta em strings. Essa ida e volta tem seu próprio conjunto de utilitários e armadilhas. Continue em Conversões de String em Java.

Prática

Prática
Qual das alternativas a seguir representa uma **diferença comportamental real** entre `StringTokenizer` e `String.split`?
Qual das alternativas a seguir representa uma **diferença comportamental real** entre `StringTokenizer` e `String.split`?
Was this page helpful?