扫描仪与StringTokenizer与String.Split

我刚刚了解了Java的Scanner类,现在我想知道它如何与StringTokenizer和String.Split进行比较/竞争。 我知道StringTokenizer和String.Split只能在字符串上工作,所以我为什么要使用扫描器的字符串? Scanner只是为了一站式购物而分拆?

他们基本上是马匹的课程。

  • Scanner设计用于您需要解析字符串,提取不同类型数据的情况。 这是非常灵活的,但可以说没有给你最简单的API来获取由特定表达式分隔的字符串数组。
  • String.split()Pattern.split()为后者提供了一个简单的语法,但这基本上就是他们所做的一切。 如果你想分析结果字符串,或根据特定的标记中途改变分隔符,他们将不会帮助你。
  • StringTokenizerString.split()更具有限制性,而且使用起来也有一点小巧。 它本质上是为了拉出由固定的子字符串分隔的令牌而设计的。 由于这个限制,它大约是String.split()两倍。 (请参阅我的比较String.split()StringTokenizer 。)它也早于正则表达式API,其中String.split()是一个部分。

从我的定时中可以看出, String.split()仍然可以在典型的机器上以几毫秒的时间标记数千个字符串 。 另外,它比StringTokenizer的优势在于它将字符串数组作为输出,这通常是你想要的。 使用StringTokenizer提供的Enumeration在大多数情况下也是“语法上的挑剔”。 从这个角度来看, StringTokenizer现在有点浪费空间,你也可以使用String.split()

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

StringTokenizer是一个遗留的类,为了兼容性的原因被保留下来,尽管在新代码中不鼓励使用它。 建议任何需要此功能的人使用Stringjava.util.regex包的split方法。

所以我们马上把它扔掉。 那离开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()几乎是一件容易的事情。

StringTokenizer总是在那里。 这是所有人中速度最快的,但是枚举式的成语看起来可能不像其他人那么优雅。

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

扫描仪来到JDK 1.5。 它是Java API中最灵活和最长久的差距,可以支持相当于着名的Cs scanf函数族。

分割速度很慢,但不像扫描仪那么慢。 StringTokenizer比拆分快。 然而,我发现我可以通过交易一些灵活性来获得双倍的速度,以获得提速,这是我在JFastParser上做的https://github.com/hughperkins/jfastparser

测试一个包含一百万双的字符串:

 Scanner: 10642 ms Split: 715 ms StringTokenizer: 544ms JFastParser: 290ms 

如果你有一个字符串对象,你想要标记化,请使用String的分解方法来使用StringTokenizer。 如果您正在从程序之外的源(例如文件或用户)解析文本数据,则Scanner派上用场。

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

http://eblog.chrononsystems.com/hidden-evils-of-javas-stringsplit-and-stringr

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

String.split似乎比StringTokenizer慢得多。 分割的唯一好处是你得到了一个令牌数组。 你也可以在分割中使用任何正则表达式。 org.apache.commons.lang.StringUtils有一个拆分方法,它的工作速度比任何两个更快。 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; } 

使用“abc”.toCharArray()来获取字符串的字符数组。 例如:

 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一样。

String.split()工作的非常好,但是有自己的界限,就像你想要根据单个或双个管道(|)符号分割一个字符串一样,它不起作用。 在这种情况下,你可以使用StringTokenizer。

ABC | IJK