163

我刚刚了解了 Java 的 Scanner 类,现在我想知道它如何与 StringTokenizer 和 String.Split 进行比较/竞争。我知道 StringTokenizer 和 String.Split 仅适用于字符串,那么我为什么要使用 Scanner 处理字符串呢?Scanner 是否只是为拆分提供一站式服务?

4

10 回答 10

246

它们本质上是课程的马。

  • Scanner专为需要解析字符串、提取不同类型数据的情况而设计。它非常灵活,但可以说没有为您提供最简单的 API 来简单地获取由特定表达式分隔的字符串数组。
  • String.split()Pattern.split()为您提供执行后者的简单语法,但这基本上就是他们所做的一切。如果您想解析生成的字符串,或者根据特定标记在中途更改分隔符,它们将无助于您。
  • StringTokenizer比 更具限制性String.split(),而且使用起来也有点复杂。它本质上是为提取由固定子字符串分隔的标记而设计的。由于这个限制,它的速度大约是String.split(). (参见我对和的比较String.split()StringTokenizer。)它还早于正则表达式 API,后者String.split()是其中的一部分。

您会从我的计时中注意到,在典型机器上仍然可以在几毫秒内String.split()标记数千个字符串。此外,它的优势在于它将输出作为字符串数组提供给您,这通常是您想要的。大多数情况下,使用由 提供的过于“语法上的挑剔”。从这个角度来看,现在有点浪费空间,你还不如直接使用.StringTokenizerEnumerationStringTokenizerStringTokenizerString.split()

于 2009-03-27T19:40:52.220 回答
58

让我们从消除StringTokenizer. 它变老了,甚至不支持正则表达式。其文档指出:

StringTokenizer是一个遗留类,出于兼容性原因保留,但不鼓励在新代码中使用它。建议任何寻求此功能的人使用split方法Stringjava.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()

于 2009-03-27T19:58:41.630 回答
9

StringTokenizer 一直都在。它是最快的,但类似枚举的习语可能看起来不像其他习语那么优雅。

split 在 JDK 1.4 上出现。比标记器慢但更易于使用,因为它可以从 String 类中调用。

Scanner 出现在 JDK 1.5 上。它是最灵活的,并且填补了 Java API 中长期存在的空白,以支持与著名的 Cs scanf 函数系列等效的功能。

于 2009-03-27T19:58:23.613 回答
7

Split 很慢,但没有 Scanner 慢。StringTokenizer 比拆分更快。然而,我发现我可以通过交易一些灵活性来获得双倍的速度,以获得速度提升,这是我在 JFastParser https://github.com/hughperkins/jfastparser所做的

对包含一百万个双精度的字符串进行测试:

Scanner: 10642 ms
Split: 715 ms
StringTokenizer: 544ms
JFastParser: 290ms
于 2012-10-11T02:59:51.883 回答
6

如果你有一个要标记的 String 对象,请优先使用 String 的split方法而不是 StringTokenizer。如果您要从程序外部的源(例如文件或用户)解析文本数据,那么扫描器就派上用场了。

于 2009-03-27T19:41:53.417 回答
4

String.split 似乎比 StringTokenizer 慢得多。split 的唯一优点是您可以获得一个令牌数组。您也可以在拆分中使用任何正则表达式。org.apache.commons.lang.StringUtils 有一个 split 方法,它比两个中的任何一个都快得多。StringTokenizer 或 String.split。但三者的 CPU 利用率几乎相同。因此,我们还需要一种 CPU 密集度较低的方法,但我仍然找不到。

于 2009-11-05T07:50:40.303 回答
4

我最近做了一些关于 String.split() 在高性能敏感情况下性能不佳的实验。您可能会发现这很有用。

Java 的 String.split() 和 replace() 隐藏的弊端

要点是 String.split() 每次都会编译正则表达式模式,因此与使用预编译的 Pattern 对象并直接使用它来操作字符串相比,它会减慢程序的速度。

于 2012-01-26T09:48:54.907 回答
2

一个重要的区别是 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。

于 2016-04-21T16:27:34.830 回答
1

对于默认情况,我也建议使用 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 s = "     a bb   ccc  dddd eeeee  ffffff    ggggggg ";
ArrayList<String> result = splitBySingleChar(s.toCharArray(), ' ');
于 2015-03-12T11:01:47.953 回答
-5

String.split() 效果很好,但有自己的界限,比如如果你想根据单管道或双管道 (|) 符号拆分字符串,如下所示,它不起作用。在这种情况下,您可以使用 StringTokenizer。

ABC|IJK

于 2013-02-22T22:09:59.897 回答