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
// cherryO 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
// fourEssa é 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
// *
// cEsse é 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(); // 0Isso 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.
StringTokenizersó trata delimitadores de um único caractere. - Tokens vazios são visíveis.
"a,,b".split(",")retorna["a", "", "b"].StringTokenizerignora 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
Patternpara 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.
StringTokenizernão aloca o resultado antecipadamente;splitsim. - 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:
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.