经过修改StringTokenizer
班后,我无法找到一种方法来满足返回["dog", "", "cat"]
的要求。
此外,StringTokenizer
级仅出于兼容性的原因,并鼓励使用String.split
。从用于StringTokenizer
的API规格:
StringTokenizer
是传统类 ,但留作兼容性 原因虽然不鼓励使用在新代码 。建议任何寻求此功能的人都使用split
方法 或String
或java.util.regex
包代替。
由于问题是String.split
方法的性能差,我们需要找到一个替代方案。
注:我说:“按说表现不佳”,因为很难确定每个用例将会导致StringTokenizer
是优于String.split
方法。此外,在很多情况下,除非字符串的标记化确实是通过适当的分析确定的应用程序的瓶颈,否则我觉得如果有的话,它最终会成为不成熟的优化。我倾向于说在写入优化之前编写有意义且易于理解的代码。
现在,从目前的要求来看,可能滚动我们自己的标记器并不会太困难。
滚动我们自己的令牌机!
以下是我写的一个简单的分词器。我要指出,没有速度的优化,也没有错误检查,以防止打算过去字符串的结尾 - 这是一个快速和肮脏的实现:
class MyTokenizer implements Iterable<String>, Iterator<String> {
String delim = ",";
String s;
int curIndex = 0;
int nextIndex = 0;
boolean nextIsLastToken = false;
public MyTokenizer(String s, String delim) {
this.s = s;
this.delim = delim;
}
public Iterator<String> iterator() {
return this;
}
public boolean hasNext() {
nextIndex = s.indexOf(delim, curIndex);
if (nextIsLastToken)
return false;
if (nextIndex == -1)
nextIsLastToken = true;
return true;
}
public String next() {
if (nextIndex == -1)
nextIndex = s.length();
String token = s.substring(curIndex, nextIndex);
curIndex = nextIndex + 1;
return token;
}
public void remove() {
throw new UnsupportedOperationException();
}
}
的MyTokenizer
将采取String
标记并将String
作为分隔符,并使用String.indexOf
方法执行分隔符的搜索。令牌由String.substring
方法生成。
我想通过在char[]
级别而不是String
级别处理字符串可能会有一些性能改进。但是我会把它作为练习留给读者。
类也为了充分利用这是在Java 5中StringTokenizer
推出for-each
循环结构的实现Iterable
和Iterator
是Enumerator
,并且不支持for-each
结构。
它有什么更快吗?
为了搞清楚这是更快了,我写了一个程序在以下四种方法来比较速度:
- 使用
StringTokenizer
。
- 使用新的
MyTokenizer
。
- 使用
String.split
。
- 使用
Pattern.compile
预编译的正则表达式。
在四种方法中,字符串"dog,,cat"
被分隔为标记。虽然比较中包含StringTokenizer
,但应注意的是,它不会返回["dog", "", "cat]
的预期结果。
标记化重复了总共100万次,给了足够的时间来注意方法的差异。
用于简单的基准代码是下面的:
long st = System.currentTimeMillis();
for (int i = 0; i < 1e6; i++) {
StringTokenizer t = new StringTokenizer("dog,,cat", ",");
while (t.hasMoreTokens()) {
t.nextToken();
}
}
System.out.println(System.currentTimeMillis() - st);
st = System.currentTimeMillis();
for (int i = 0; i < 1e6; i++) {
MyTokenizer mt = new MyTokenizer("dog,,cat", ",");
for (String t : mt) {
}
}
System.out.println(System.currentTimeMillis() - st);
st = System.currentTimeMillis();
for (int i = 0; i < 1e6; i++) {
String[] tokens = "dog,,cat".split(",");
for (String t : tokens) {
}
}
System.out.println(System.currentTimeMillis() - st);
st = System.currentTimeMillis();
Pattern p = Pattern.compile(",");
for (int i = 0; i < 1e6; i++) {
String[] tokens = p.split("dog,,cat");
for (String t : tokens) {
}
}
System.out.println(System.currentTimeMillis() - st);
结果
试验是使用Java SE 6运行(建立1.6.0_12-B04),和结果以下内容:
Run 1 Run 2 Run 3 Run 4 Run 5
----- ----- ----- ----- -----
StringTokenizer 172 188 187 172 172
MyTokenizer 234 234 235 234 235
String.split 1172 1156 1171 1172 1156
Pattern.compile 906 891 891 907 906
所以,可以从有限的测试中可以看出只有5次运行期间,StringTokenizer
其实ç做欧米出局最快,但MyTokenizer
进入第二。然后,String.split
是最慢的,并且预编译的正则表达式比split
方法稍快。
与任何小基准一样,它可能不是真实生活条件的代表,所以结果应该与盐的谷物(或丘)一起拍摄。
+1,我喜欢实施Iterable的想法! –
coobird
2009-06-12 13:13:51
谢谢乔恩,我手工解析(使用大量的indexof),现在它快了x4! – Dani 2009-06-12 13:56:03