Scanner vs. StringTokenizer vs. String.Split
je viens d'apprendre à propos de la classe de Scanner Java et maintenant je me demande comment il se compare/rivalise avec le StringTokenizer et la chaîne.Split. Je sais que le StringTokenizer et String.Split ne fonctionne que sur les cordes, alors pourquoi voudrais-je utiliser le Scanner pour une corde? Le Scanner est-il seulement destiné à être un guichet unique pour la division?
10 réponses
ce sont essentiellement des chevaux pour les courses.
-
Scanner
est conçu pour les cas où vous avez besoin de parser une chaîne, en tirant des données de différents types. C'est très flexible, mais sans doute ne vous donne pas L'API la plus simple pour obtenir simplement un tableau de chaînes délimitées par une expression particulière. -
String.split()
etPattern.split()
donnez-vous une syntaxe facile à utiliser pour faire le dernier, mais c'est essentiellement tout ce qu'ils font. Si vous voulez analyser les chaînes résultantes, ou changer le délimiteur à mi-chemin en fonction d'un token particulier, ils ne vous aideront pas avec cela. -
StringTokenizer
est encore plus restrictif queString.split()
, et aussi un peu plus compliqué à utiliser. Il est essentiellement conçu pour retirer des jetons délimités par des substrats fixes. En raison de cette restriction, il est environ deux fois plus rapide queString.split()
. (Voir ma comparaison deString.split()
etStringTokenizer
" .) Elle précède également les expressions régulières API, dontString.split()
fait partie.
vous noterez d'après mes timings que String.split()
peut encore tokenize des milliers de cordes en quelques millisecondes sur une machine typique. En outre, il a l'avantage sur StringTokenizer
qu'il vous donne la sortie comme un tableau de corde, qui est habituellement ce que vous voulez. Utiliser un Enumeration
, comme prévu par StringTokenizer
, est trop " syntaxiquement pointilleux" la plupart du temps. De ce point de vue, StringTokenizer
est un peu du gaspillage de l'espace de nos jours, et vous pouvez aussi bien utiliser String.split()
.
commençons par éliminer StringTokenizer
. Il devient vieux et ne supporte même pas les expressions régulières. Sa documentation stipule:
StringTokenizer
est une classe héritée qui est conservée pour des raisons de compatibilité bien que son utilisation soit déconseillée dans le nouveau code. Il est recommandé à toute personne recherchant cette fonctionnalité d'utiliser la méthodesplit
deString
ou le paquetjava.util.regex
à la place.
alors jetons-le tout de suite. Il reste donc split()
et Scanner
. Quelle est la différence entre eux?
pour une chose, split()
renvoie simplement un tableau, ce qui rend facile d'utiliser une boucle foreach:
for (String token : input.split("\s+") { ... }
Scanner
est construit plus comme un ruisseau:
while (myScanner.hasNext()) {
String token = myScanner.next();
...
}
ou
while (myScanner.hasNextDouble()) {
double token = myScanner.nextDouble();
...
}
(il a un plutôt grande API , alors ne pensez pas qu'il est toujours limité à des choses aussi simples.)
cette interface stream-style peut être utile pour analyser des fichiers texte simples ou des entrées de console, quand vous n'avez pas (ou ne pouvez pas obtenir) toutes les entrées avant de commencer à analyser.
personnellement, la seule fois où je me souviens d'avoir utilisé Scanner
, c'est pour des projets scolaires, lorsque j'ai dû obtenir les données de l'utilisateur à partir de la ligne de commande. Il qui rend l'opération facile. Mais si j'ai un String
que je veux séparer, c'est presque une évidence pour aller avec split()
.
StringTokenizer était toujours là. Il est le plus rapide de tous, mais l'énumération de l'idiome pourrait ne pas sembler aussi élégant que les autres.
split a vu le jour sur JDK 1.4. Plus lent que tokenizer mais plus facile à utiliser, puisqu'il est appelable à partir de la classe String.
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 Cs scanf.
Split 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 une certaine flexibilité, pour obtenir un boost de vitesse, ce que j'ai fait à JFastParser https://github.com/hughperkins/jfastparser
test sur une corde contenant un million de doubles:
Scanner: 10642 ms
Split: 715 ms
StringTokenizer: 544ms
JFastParser: 290ms
si vous avez un objet String que vous voulez tokenize, favorisez en utilisant la méthode split plutôt qu'un StringTokenizer. Si vous analysez des données texte d'une source en dehors de votre programme, comme un fichier, ou de l'utilisateur, c'est là qu'un Scanner est pratique.
de la Chaîne.split semble être beaucoup plus lent que StringTokenizer. Le seul avantage avec split est que vous obtenez un tableau des jetons. Vous pouvez aussi utiliser n'importe quelle expression régulière dans split. org.Apache.commun.lang.StringUtils a une méthode split qui fonctionne beaucoup plus rapidement que n'importe laquelle des deux viz. StringTokenizer ou String.Split. Mais L'utilisation CPU pour les trois est presque la même. Nous avons donc également besoin d'une méthode moins intensive en CPU, que je ne suis toujours pas en mesure de trouver.
j'ai récemment fait quelques expériences sur la mauvaise performance de la Chaîne.split () dans les situations très sensibles aux performances. Vous pourriez trouver cela utile.
http://eblog.chrononsystems.com/hidden-evils-of-javas-stringsplit-and-stringr
l'essentiel est cette chaîne.split() compile un pattern D'Expression régulier à chaque fois et peut donc ralentir votre programme, comparé à si vous utilisez un objet Pattern précompilé et utilisez il fonctionne directement sur une corde.
pour les scénarios par défaut, je suggérerais un modèle.split() aussi bien que split () mais si vous avez besoin de performances maximales (en particulier sur Android toutes les solutions que j'ai testé sont assez lents) et que vous n'avez besoin de split 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 char tableau pour une Chaîne de caractères. Par exemple:
String s = " a bb ccc dddd eeeee ffffff ggggggg ";
ArrayList<String> result = splitBySingleChar(s.toCharArray(), ' ');
une différence importante est que les deux chaînes.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
c'est parce que le délimiteur pour la chaîne.split () et Scanner.useDelimiter () n'est pas seulement une chaîne, mais une expression régulière. Nous pouvons remplacer le séparateur ", "avec" +" dans l'exemple ci-dessus pour les faire se comporter comme des StringTokenizer.
de la Chaîne.split () fonctionne très bien mais a ses propres limites, comme si vous vouliez fendre une chaîne comme montré ci-dessous basé sur le symbole Single ou double pipe ( | ), il ne fonctionne pas. Dans cette situation, vous pouvez utiliser StringTokenizer.
ABC /IJK