我刚刚学习了Java的Scanner类,现在我想知道它是如何与StringTokenizer和String.Split进行比较/竞争的。我知道StringTokenizer和String.Split只适用于字符串,那么为什么我要将Scanner用于字符串呢?扫描仪是否只是一次性购物分裂?
答案 0 :(得分:233)
他们基本上是马匹的课程。
Scanner
适用于需要解析字符串,提取不同类型数据的情况。它非常灵活,但可以说是没有为您提供最简单的API来简单地获取由特定表达式分隔的字符串数组。String.split()
和Pattern.split()
为您提供了一个简单的语法来完成后者,但这基本上就是他们所做的一切。如果您想要解析生成的字符串,或者根据特定令牌中途更改分隔符,他们将无法帮助您。StringTokenizer
比String.split()
更具限制性,而且使用起来也有些笨拙。它主要用于拉出由固定子串分隔的令牌。由于这种限制,它的速度大约是String.split()
的两倍。 (请参阅我的comparison of String.split()
and StringTokenizer
。)它也早于正则表达式API,其中String.split()
是其中的一部分。您会从我的时间中注意到,String.split()
仍然可以在典型计算机上将数千个字符串标记为几毫秒。此外,它具有优于StringTokenizer
的优势,它可以将输出作为字符串数组提供,这通常是您想要的。使用由Enumeration
提供的StringTokenizer
,在大多数情况下过于“语法上挑剔”。从这个角度来看,StringTokenizer
现在有点浪费空间,你也可以使用String.split()
。
答案 1 :(得分:57)
让我们从消除StringTokenizer
开始。它变老了,甚至不支持正则表达式。其文件说明:
StringTokenizer
是一个遗留类,出于兼容性原因而保留,尽管在新代码中不鼓励使用它。建议所有寻求此功能的人使用split
String
方法或java.util.regex
包。
所以让我们马上扔掉它。留下split()
和Scanner
。它们之间有什么区别?
首先,split()
只返回一个数组,这样可以很容易地使用foreach循环:
for (String token : input.split("\\s+") { ... }
Scanner
更像是一个流:
while (myScanner.hasNext()) {
String token = myScanner.next();
...
}
或
while (myScanner.hasNextDouble()) {
double token = myScanner.nextDouble();
...
}
(它有一个large API,所以不要认为它总是局限于这么简单的事情。)
当您在开始解析之前没有(或无法获取)所有输入时,此流式接口可用于解析简单文本文件或控制台输入。
就个人而言,我唯一能记住使用Scanner
的时间用于学校项目,当我必须从命令行获取用户输入时。它使这种操作变得容易。但是,如果我想要分割String
,那么与split()
一起使用几乎是明智的。
答案 2 :(得分:9)
StringTokenizer总是在那里。它是最快的,但类似枚举的习语可能看起来不像其他的那样优雅。
分裂在JDK 1.4上出现。比tokenizer慢但更容易使用,因为它可以从String类调用。
扫描仪开始使用JDK 1.5。它是最灵活的,填补了Java API的长期缺口,以支持相当于着名的Cs scanf函数系列。
答案 3 :(得分:6)
分割很慢,但不像扫描仪那么慢。 StringTokenizer比split更快。然而,我发现通过交换一些灵活性,我可以获得双倍的速度,以获得速度提升,我在JFastParser https://github.com/hughperkins/jfastparser
测试包含一百万个双打的字符串:
Scanner: 10642 ms
Split: 715 ms
StringTokenizer: 544ms
JFastParser: 290ms
答案 4 :(得分:5)
如果您想要标记化String对象,请使用String的split方法而不是StringTokenizer。如果您正在从程序外部的源(例如文件或用户)解析文本数据,那么扫描仪就会派上用场。
答案 5 :(得分:4)
String.split似乎比StringTokenizer慢得多。拆分的唯一优势是你得到一个令牌数组。您还可以在拆分中使用任何正则表达式。 org.apache.commons.lang.StringUtils有一个split方法,它比两个中的任何一个都快得多。 StringTokenizer或String.split。 但是这三者的CPU利用率几乎相同。所以我们还需要一种CPU密集度较低的方法,但我仍然无法找到。
答案 6 :(得分:4)
我最近在高性能敏感的情况下做了一些关于String.split()性能不佳的实验。你可能会觉得这很有用。
http://eblog.chrononsystems.com/hidden-evils-of-javas-stringsplit-and-stringr
要点是String.split()每次编译一个正则表达式模式,因此与使用预编译的Pattern对象并直接使用它来操作String相比,可以减慢程序的速度。
答案 7 :(得分:1)
对于默认场景我也建议使用Pattern.split()但是如果你需要最大的性能(特别是在Android上,我测试的所有解决方案都很慢)你只需要用一个char分割,我现在用我的自己的方法:
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;
}
使用&#34; abc&#34; .toCharArray()获取String的char数组。例如:
String s = " a bb ccc dddd eeeee ffffff ggggggg ";
ArrayList<String> result = splitBySingleChar(s.toCharArray(), ' ');
答案 8 :(得分:1)
一个重要的区别是String.split()和Scanner都可以生成空字符串,但StringTokenizer永远不会这样做。
例如:
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());
输出:
//StringTokenizer
#0: ab
#1: cd
#2: ef
//String.split()
#0: ab
#1: cd
#2:
#3: ef
//Scanner
#0: ab
#1: cd
#2:
#3: ef
这是因为String.split()和Scanner.useDelimiter()的分隔符不仅仅是一个字符串,而是一个正则表达式。我们可以替换分隔符&#34; &#34;用&#34; +&#34;在上面的示例中,使它们的行为类似于StringTokenizer。
答案 9 :(得分:-6)
String.split()工作得很好但有自己的边界,就像你想根据单管或双管(|)符号分割如下所示的字符串,它不起作用。在这种情况下,您可以使用StringTokenizer。
ABC | IJK