經過修改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