web-dev-qa-db-fra.com

Scanner vs StringTokenizer vs String.Split

Je viens tout juste d'apprendre la classe Scanner de Java et je me demande maintenant comment elle se compare/rivalise avec StringTokenizer et String.Split. Je sais que StringTokenizer et String.Split ne fonctionnent que sur des chaînes, alors pourquoi voudrais-je utiliser le scanner pour une chaîne? Est-ce que Scanner est simplement destiné à être un guichet unique pour le fractionnement?

150
Dave

Ce sont essentiellement des chevaux pour les cours.

  • Scanner est conçu pour les cas où vous devez analyser une chaîne en extrayant des données de types différents. C'est très flexible, mais on ne peut sans doute pas vous donner l'API la plus simple pour obtenir simplement un tableau de chaînes délimité par une expression particulière.
  • String.split() et Pattern.split() vous donnent une syntaxe simple pour effectuer cette opération, mais c'est essentiellement tout ce qu'ils font. Si vous souhaitez analyser les chaînes résultantes ou modifier le délimiteur à mi-parcours en fonction d'un jeton particulier, ils ne vous aideront pas.
  • StringTokenizer est encore plus restrictif que String.split(), et aussi un peu plus difficile à utiliser. Il est essentiellement conçu pour extraire des jetons délimités par des sous-chaînes fixes. En raison de cette restriction, il est environ deux fois plus rapide que String.split(). (Voir mon comparaison de String.split() et StringTokenizer .) Il est également antérieur à l'API d'expressions régulières, dont String.split() fait partie.

Vous noterez d'après mes timings que String.split() peut encore marquer des milliers de chaînes en quelques millisecondes sur une machine typique. De plus, il a l'avantage sur StringTokenizer de vous donner la sortie sous forme de tableau de chaînes, ce qui correspond généralement à ce que vous voulez. L'utilisation de Enumeration, telle que fournie par StringTokenizer, est trop "syntaxiquement difficile" la plupart du temps. De ce point de vue, StringTokenizer est un peu une perte d’espace de nos jours, et vous pouvez aussi bien utiliser String.split().

236
Neil Coffey

Commençons par éliminer StringTokenizer . Il vieillit et ne supporte même pas les expressions régulières. Sa documentation indique:

StringTokenizer est une classe héritée conservée pour des raisons de compatibilité bien que son utilisation soit découragée dans le nouveau code. Il est recommandé à ceux qui recherchent cette fonctionnalité d’utiliser plutôt la méthode split de String ou le package Java.util.regex.

Alors jetons-le tout de suite. Cela laisse split() et Scanner . Quelle est la différence entre eux?

D'une part, split() renvoie simplement un tableau, ce qui facilite l'utilisation d'une boucle foreach:

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

Scanner est construit plus comme un flux:

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

ou

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

(Il a plutôt une grande API , alors ne croyez pas que c'est toujours limité à de telles choses simples.)

Cette interface de style flux peut être utile pour analyser des fichiers texte simples ou des entrées de console, lorsque vous n'avez pas (ou ne pouvez pas obtenir) toutes les entrées avant de commencer l'analyse.

Personnellement, la seule fois où je me souviens de l’utilisation de Scanner concerne des projets d’école, lorsque je devais obtenir les informations de l’utilisateur à partir de la ligne de commande. Cela facilite ce genre d'opération. Mais si j'ai un String que je souhaite séparer, c’est presque une évidence de partir avec split().

57
Michael Myers

StringTokenizer était toujours là. C'est le plus rapide de tous, mais le langage de type énumération pourrait ne pas sembler aussi élégant que les autres.

la scission est née le JDK 1.4. Plus lent que tokenizer mais plus facile à utiliser, puisqu'il est appelable depuis la classe String.

Le scanner est venu pour être sur JDK 1.5. Il est le plus flexible et comble une lacune de longue date sur l’API Java) pour prendre en charge un équivalent de la célèbre famille de fonctions scanf de Cs.

9
H Marcelo Morales

Le fractionnement est lent, mais pas aussi lent que le scanner. StringTokenizer est plus rapide que split. Cependant, j'ai trouvé que je pouvais obtenir le double de la vitesse, en échangeant un peu de flexibilité, pour obtenir un gain de vitesse, ce que j'ai fait chez JFastParser https://github.com/hughperkins/jfastparser

Test sur une chaîne contenant un million de doubles:

Scanner: 10642 ms
Split: 715 ms
StringTokenizer: 544ms
JFastParser: 290ms
6
Hugh Perkins

Si vous souhaitez tokeniser un objet String, privilégiez l'utilisation de la méthode split de String par rapport à StringTokenizer. Si vous analysez des données texte provenant d'une source extérieure à votre programme, par exemple d'un fichier, ou de l'utilisateur, c'est là qu'un scanner est utile.

5
Bill the Lizard

String.split semble être beaucoup plus lent que StringTokenizer. Le seul avantage de la division est que vous obtenez un tableau des jetons. Aussi, vous pouvez utiliser toutes les expressions régulières en split. org.Apache.commons.lang.StringUtils a une méthode split qui fonctionne beaucoup plus vite que n'importe lequel des deux. StringTokenizer ou String.split. Mais l'utilisation du processeur pour les trois est presque la même. Nous avons donc également besoin d’une méthode moins gourmande en ressources processeur, que je ne suis toujours pas en mesure de trouver.

4
Manish

J'ai récemment effectué quelques expériences sur les mauvaises performances de String.split () dans des situations extrêmement sensibles aux performances. Vous pouvez trouver cela utile.

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

En résumé, String.split () compile à chaque fois un motif d’expression régulière et peut donc ralentir votre programme, contrairement à un objet Pattern précompilé que vous utilisiez directement pour opérer sur une chaîne.

4
pdeva

Une différence importante est que String.split () et Scanner peuvent produire des chaînes vides, mais StringTokenizer ne le fait jamais.

Par exemple:

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

Sortie:

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

En effet, le délimiteur de String.split () et Scanner.useDelimiter () n'est pas simplement une chaîne, mais une expression régulière. Nous pouvons remplacer le délimiteur "" par "+" dans l'exemple ci-dessus pour qu'ils se comportent comme StringTokenizer.

1
John29

Pour les scénarios par défaut, je suggérerais également Pattern.split (), mais si vous avez besoin de performances maximales (en particulier sur Android toutes les solutions que j'ai testées sont assez lentes)), il vous suffit de diviser par un seul. char, j'utilise maintenant ma propre méthode:

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

Utilisez "abc" .toCharArray () pour obtenir le tableau de caractères pour une chaîne. Par exemple:

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