Pergunta

Acabei de aprender sobre a classe Scanner de Java e agora estou me perguntando como ela se compara/compete com o StringTokenizer e String.split. Eu sei que o StringTokenizer e String.Split funcionam apenas em strings, então por que eu gostaria de usar o scanner para uma string? O scanner pretende ser um balcão único para dividir?

Foi útil?

Solução

Eles são essencialmente cavalos para cursos.

  • Scanner foi projetado para casos em que você precisa analisar uma string, retirando dados de diferentes tipos. É muito flexível, mas sem dúvida não oferece a API mais simples para simplesmente obter uma variedade de cordas delimitadas por uma expressão específica.
  • String.split() e Pattern.split() Dê a você uma sintaxe fácil para fazer o último, mas isso é essencialmente tudo o que eles fazem. Se você deseja analisar as cordas resultantes ou alterar o delimitador no meio do caminho, dependendo de um token específico, eles não o ajudarão com isso.
  • StringTokenizer é ainda mais restritivo do que String.split(), e também um pouco fiddlier de usar. É essencialmente projetado para extrair tokens delimitados por substringas fixas. Por causa dessa restrição, é cerca de duas vezes mais rápido que String.split(). (Veja meu comparação de String.split() e StringTokenizer.) Também antecede a API de expressões regulares, da qual String.split() é uma parte.

Você notará dos meus horários que String.split() ainda posso tokenizar milhares de cordas em alguns milissegundos em uma máquina típica. Além disso, tem a vantagem sobre StringTokenizer que fornece a saída como uma matriz de string, que geralmente é o que você deseja. Usando um Enumeration, conforme fornecido por StringTokenizer, é muito "sintaticamente exigente" na maioria das vezes. A partir deste ponto de vista, StringTokenizer é um pouco de desperdício de espaço hoje em dia, e você também pode usar String.split().

Outras dicas

Vamos começar eliminando StringTokenizer. Está envelhecendo e nem suporta expressões regulares. Sua documentação afirma:

StringTokenizer é uma classe herdada retida por razões de compatibilidade, embora seu uso seja desencorajado em um novo código. Recomenda -se que qualquer pessoa que procure essa funcionalidade use o split método de String ou o java.util.regex Em vez disso, pacote.

Então, vamos jogar fora imediatamente. Isso sai split() e Scanner. Qual é a diferença entre eles?

Por uma coisa, split() Simplesmente retorna uma matriz, o que facilita o uso de um loop foreach:

for (String token : input.split("\\s+") { ... }

Scanner é construído mais como um fluxo:

while (myScanner.hasNext()) {
    String token = myScanner.next();
    ...
}

ou

while (myScanner.hasNextDouble()) {
    double token = myScanner.nextDouble();
    ...
}

(Tem um pouco API grande, então não pense que sempre se restringe a coisas tão simples.)

Essa interface no estilo de fluxo pode ser útil para analisar arquivos de texto simples ou entrada do console, quando você não tiver (ou não conseguir) toda a entrada antes de começar a analisar.

Pessoalmente, a única vez que me lembro de usar Scanner é para projetos escolares, quando tive que obter a entrada do usuário da linha de comando. Isso facilita esse tipo de operação. Mas se eu tiver um String Que eu quero me separar, é quase um acéfalo para ir com split().

StringTokenizer estava sempre lá. É o mais rápido de todos, mas o idioma do tipo enumeração pode não parecer tão elegante quanto os outros.

Split veio à existência no JDK 1.4. Mais lento que o tokenizer, mas mais fácil de usar, pois é chamável da classe String.

O scanner chegou a estar no JDK 1.5. É a mais flexível e preenche uma lacuna de longa data na API Java para apoiar um equivalente à famosa família de funções do SCANF CS.

Split é lento, mas não tão lento quanto o scanner. StringTokenizer é mais rápido que dividido. No entanto, descobri que poderia obter o dobro da velocidade, negociando alguma flexibilidade, para obter um salto de velocidade, o que fiz no JFastParser https://github.com/hughperkins/jfastparser

Teste em uma string contendo um milhão de duplas:

Scanner: 10642 ms
Split: 715 ms
StringTokenizer: 544ms
JFastParser: 290ms

Se você tem um objeto de string que deseja tokenize, favorece usando o String's dividir Método sobre um StringTokenizer. Se você estiver analisando os dados de texto de uma fonte fora do seu programa, como de um arquivo ou do usuário, é aí que um scanner é útil.

String.split parece ser muito mais lento que o StringTokenizer. A única vantagem com Split é que você recebe uma matriz dos tokens. Além disso, você pode usar quaisquer expressões regulares em divisão. org.apache.commons.lang.stringutils possui um método dividido que funciona muito mais mais rápido do que qualquer um dos dois viz. StringTokenizer ou String.split. Mas a utilização da CPU para todos os três é quase a mesma. Portanto, também precisamos de um método que seja menos intensivo na CPU, que ainda não consigo encontrar.

Recentemente, fiz algumas experiências sobre o mau desempenho de String.split () em situações sensíveis ao desempenho. Você pode achar isso útil.

http://eblog.chrononsystems.com/hidden-evils-of-javas-stringsplit-and-stringr

O GIST é que o string.split () compila um padrão de expressão regular a cada vez e, portanto, pode desacelerar seu programa, em comparação se você usar um objeto de padrão pré -compilado e usá -lo diretamente para operar em uma string.

Para os cenários padrão, eu sugeriria Pattern.split () também, mas se você precisar de desempenho máximo (especialmente no Android, todas as soluções que testei são bastante lentas) e você só precisa dividir por um único char, agora uso meu próprio método:

public static ArrayList<String> splitBySingleChar(final char[] s,
        final char splitChar) {
    final ArrayList<String> result = new ArrayList<String>();
    final int length = s.length;
    int offset = 0;
    int count = 0;
    for (int i = 0; i < length; i++) {
        if (s[i] == splitChar) {
            if (count > 0) {
                result.add(new String(s, offset, count));
            }
            offset = i + 1;
            count = 0;
        } else {
            count++;
        }
    }
    if (count > 0) {
        result.add(new String(s, offset, count));
    }
    return result;
}

Use "ABC" .ToCharArray () para obter a matriz CHAR para uma string. Por exemplo:

String s = "     a bb   ccc  dddd eeeee  ffffff    ggggggg ";
ArrayList<String> result = splitBySingleChar(s.toCharArray(), ' ');

Uma diferença importante é que a string.split () e o scanner podem produzir strings vazios, mas o StringTokenizer nunca o faz.

Por exemplo:

String str = "ab cd  ef";

StringTokenizer st = new StringTokenizer(str, " ");
for (int i = 0; st.hasMoreTokens(); i++) System.out.println("#" + i + ": " + st.nextToken());

String[] split = str.split(" ");
for (int i = 0; i < split.length; i++) System.out.println("#" + i + ": " + split[i]);

Scanner sc = new Scanner(str).useDelimiter(" ");
for (int i = 0; sc.hasNext(); i++) System.out.println("#" + i + ": " + sc.next());

Resultado:

//StringTokenizer
#0: ab
#1: cd
#2: ef
//String.split()
#0: ab
#1: cd
#2: 
#3: ef
//Scanner
#0: ab
#1: cd
#2: 
#3: ef

Isso ocorre porque o delimitador para string.split () e scanner.usedelimiter () não é apenas uma string, mas uma expressão regular. Podemos substituir o delimitador "" por " +" no exemplo acima para fazê -los se comportar como StringTokenizer.

String.split () funciona muito bem, mas tem seus próprios limites, como se você quisesse dividir uma string, como mostrado abaixo, com base no símbolo único ou duplo (|), ele não funciona. Nesta situação, você pode usar StringTokenizer.

ABC | IJK

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top