我刚学习了Java的Scanner类,现在想知道它与StringTokenizer和String.Split有什么不同/竞争关系。我知道StringTokenizer和String.Split只能用于字符串,那么为什么我要使用Scanner来处理字符串呢?Scanner是否只是专门用于拆分字符串的一站式工具?
我刚学习了Java的Scanner类,现在想知道它与StringTokenizer和String.Split有什么不同/竞争关系。我知道StringTokenizer和String.Split只能用于字符串,那么为什么我要使用Scanner来处理字符串呢?Scanner是否只是专门用于拆分字符串的一站式工具?
它们本质上是不同场景下的工具。
Scanner
适用于需要解析字符串并提取不同类型数据的情况。它非常灵活,但可以说在仅需获取由特定表达式分隔的字符串数组时,其API相对较为繁琐。String.split()
和 Pattern.split()
提供了一种轻松的语法来执行后者,但本质上它们只能做到这点。如果您想要解析生成的字符串,或根据特定的标记在中途更改分隔符,它们都无法帮助您实现。StringTokenizer
比 String.split()
更加受限制,使用起来也有点棘手。它基本上是设计用于提取由固定子字符串分隔的标记。正因为这种限制,它比String.split()
快约两倍。(参见我的《String.split()
和StringTokenizer
性能对比》)它还早于正则表达式API,而String.split()
是其中的一部分。从时间上可以看出,String.split()
在典型的机器上仍然可以在几毫秒内对数千个字符串进行标记化。此外,它优于StringTokenizer
的地方在于,它将输出作为字符串数组返回,这通常是您想要的结果。使用由StringTokenizer
提供的枚举类型,在大多数情况下都太过繁琐。从这个角度来看,StringTokenizer
现在有点浪费空间,你也可以直接使用String.split()
。
StringTokenizer
。它已经过时,甚至不支持正则表达式。它的文档说明如下:
所以我们立即抛弃它。这样就只剩下
StringTokenizer
是一个保留的遗留类,出于兼容性原因而保留,尽管在新代码中不建议使用它。建议寻求此功能的任何人都使用String
的split
方法或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();
...
}
(它有一个相当大的API, 所以不要认为它总是限制于这样简单的事情。)
这种流式接口在解析简单文本文件或控制台输入时非常有用,当您没有(或无法获取)所有输入时开始解析。
就我个人而言,我唯一记得使用Scanner
的时间是在学校项目中,当我需要从命令行获取用户输入时。它使得这种操作变得容易。但是,如果我有一个要分割的String
,几乎毫无疑问会选择使用split()
。
Scanner
来检测给定String
中的换行符。由于换行符可能因平台而异(请查看Pattern
的javadoc!)并且输入字符串不能保证符合System.lineSeparator()
,所以我发现Scanner
更适合,因为它已经知道在调用nextLine()
时要查找什么换行符。对于String.split
,我将不得不提供正确的正则表达式模式来检测行分隔符,但我没有发现任何标准位置存储它(我能做的最好的就是从Scanner
类的源代码中复制它)。 - ADTCStringTokenizer一直存在。它是所有方法中最快的,但类似于枚举的风格可能不像其他方法那样优雅。
split在JDK 1.4上出现。虽然比tokenizer慢,但更易于使用,因为它可以从String类调用。
Scanner在JDK 1.5上出现。它是最灵活的,并填补了Java API长期存在的空白,支持与著名的Cs scanf函数系列等效的功能。
Split虽然比Scanner慢,但并不像Scanner那么慢。StringTokenizer比split要快。然而,我发现通过牺牲一些灵活性来获得速度提升是可以使速度快出两倍的,这一点在JFastParser中实现了。https://github.com/hughperkins/jfastparser
在包含一百万个double的字符串上进行测试:
Scanner: 10642 ms
Split: 715 ms
StringTokenizer: 544ms
JFastParser: 290ms
String.split似乎比StringTokenizer慢得多。 split的唯一优点是您可以获得标记的数组。 此外,您可以在split中使用任何正则表达式。 org.apache.commons.lang.StringUtils具有split方法,其运行速度比两个分别快得多,即StringTokenizer或String.split。 但是,所有三者的CPU利用率几乎相同。 因此,我们还需要一种CPU占用较少的方法,但我仍然找不到。
对于默认情况,我建议使用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;
}
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() 的功能非常好,但它有自己的限制,例如如果您想根据单个或双重竖杠(|)符号拆分下面所示的字符串,则无法正常工作。在这种情况下,您可以使用 StringTokenizer。
ABC|IJK
StringTokenizer
是否仍然是最佳选择,因为String.split()
会简单地耗尽内存? - Sergei Tachenov