提问人:Dave 提问时间:3/28/2009 最后编辑:skaffmanDave 更新时间:12/27/2021 访问量:154573
Scanner 与 StringTokenizer 与 String.Split
Scanner vs. StringTokenizer vs. String.Split
问:
我刚刚了解了 Java 的 Scanner 类,现在我想知道它如何与 StringTokenizer 和 String.Split 进行比较/竞争。我知道 StringTokenizer 和 String.Split 仅适用于字符串,那么我为什么要对字符串使用 Scanner?Scanner 只是为了提供一站式拆分服务吗?
答:
他们本质上是课程的马。
Scanner
专为需要解析字符串、提取不同类型的数据的情况而设计。它非常灵活,但可以说没有为您提供最简单的 API,用于简单地获取由特定表达式分隔的字符串数组。String.split()
并给你一个简单的语法来做后者,但这基本上就是他们所做的一切。如果您想解析生成的字符串,或者根据特定令牌在中途更改分隔符,它们将无济于事。Pattern.split()
StringTokenizer
甚至比 更严格,而且使用起来也有点繁琐。它本质上是为拉出由固定子字符串分隔的令牌而设计的。由于此限制,它的速度大约是 的两倍。(请参阅我对String.split()
和StringTokenizer
的比较。它也早于正则表达式 API,后者是其中的一部分。String.split()
String.split()
String.split()
你会从我的计时中注意到,在典型的机器上,仍然可以在几毫秒内标记数千个字符串。此外,它的优势在于它以字符串数组的形式为您提供输出,这通常是您想要的。大多数时候,使用 提供的 是 太“语法上挑剔”了。从这个角度来看,现在有点浪费空间,还不如直接使用。String.split()
StringTokenizer
Enumeration
StringTokenizer
StringTokenizer
String.split()
评论
StringTokenizer
String.split()
如果要标记化 String 对象,请使用 String 的 split 方法而不是 StringTokenizer。如果您要解析来自程序外部源(例如文件或用户)的文本数据,那么 Scanner 就派上用场了。
评论
StringTokenizer 一直在那里。它是最快的,但类似枚举的成语可能看起来不像其他成语那样优雅。
split 在 JDK 1.4 上出现。比分词器慢,但更易于使用,因为它可以从 String 类中调用。
Scanner 出现在 JDK 1.5 上。它是最灵活的,填补了 Java API 上长期存在的空白,以支持著名的 Cs scanf 函数系列的等效功能。
让我们从消除 StringTokenizer
开始。它越来越老了,甚至不支持正则表达式。其文件指出:
StringTokenizer
是出于兼容性原因而保留的旧类,尽管不鼓励在新代码中使用该类。建议任何寻求此功能的人都改用 或 包的方法。split
String
java.util.regex
所以让我们马上把它扔掉。剩下的就是 split()
和 Scanner
。它们之间有什么区别?
首先,只需返回一个数组,这样可以很容易地使用 foreach 循环:split()
for (String token : input.split("\\s+") { ... }
Scanner
构建得更像一个流:
while (myScanner.hasNext()) {
String token = myScanner.next();
...
}
或
while (myScanner.hasNextDouble()) {
double token = myScanner.nextDouble();
...
}
(它有一个相当大的 API,所以不要以为它总是局限于这么简单的东西。
此流样式接口可用于分析简单的文本文件或控制台输入,当您在开始分析之前没有(或无法获取)所有输入时。
就我个人而言,我唯一记得使用的时间是用于学校项目,当时我必须从命令行获取用户输入。它使这种操作变得容易。但是,如果我有一个我想分手的人,那几乎是不费吹灰之力的。Scanner
String
split()
评论
Scanner
String
Pattern
System.lineSeparator()
Scanner
nextLine()
String.split
Scanner
String.split 似乎比 StringTokenizer 慢得多。拆分的唯一优点是您可以获得令牌数组。此外,您可以在拆分中使用任何正则表达式。 org.apache.commons.lang.StringUtils 有一个 split 方法,它的工作速度比两个可视化中的任何一个都快得多。StringTokenizer 或 String.split。 但是这三者的 CPU 利用率几乎相同。因此,我们还需要一种 CPU 密集度较低的方法,我仍然无法找到。
评论
我最近做了一些实验,关于 String.split() 在高度性能敏感的情况下的糟糕性能。您可能会发现这很有用。
Java 的 String.split() 和 replace() 隐藏的弊端
要点是 String.split() 每次都会编译一个正则表达式模式,因此会减慢程序的速度,而使用预编译的 Pattern 对象并直接使用它来操作 String。
评论
Split 很慢,但不像 Scanner 那么慢。StringTokenizer 比 split 更快。但是,我发现我可以通过交换一些灵活性来获得双倍的速度,从而获得速度提升,我在 JFastParser https://github.com/hughperkins/jfastparser 中做到了这一点
在包含 100 万个双精度的字符串上进行测试:
Scanner: 10642 ms
Split: 715 ms
StringTokenizer: 544ms
JFastParser: 290ms
评论
String.split() 工作得很好,但有自己的边界,比如如果你想根据单管或双管 (|) 符号拆分如下图所示的字符串,它不起作用。在这种情况下,可以使用 StringTokenizer。
美国广播公司|艾克
评论
对于默认场景,我也建议使用 Pattern.split(),但如果您需要最大性能(尤其是在 Android 上,我测试的所有解决方案都非常慢),并且您只需要拆分一个字符,我现在使用我自己的方法:
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;
}
使用 “abc”.toCharArray() 获取 String 的 char 数组。例如:
String s = " a bb ccc dddd eeeee ffffff ggggggg ";
ArrayList<String> result = splitBySingleChar(s.toCharArray(), ' ');
一个重要的区别是 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() 的分隔符不仅仅是一个字符串,而是一个正则表达式。在上面的示例中,我们可以将分隔符 “ ” 替换为 “ +”,使它们的行为类似于 StringTokenizer。
评论