クラスをいじくり回した後StringTokenizer
、私は戻るための要件を満たす方法を見つけることができませんでした["dog", "", "cat"]
。
さらに、StringTokenizer
クラスは互換性の理由でのみ残されており、の使用String.split
が推奨されています。のAPI仕様からStringTokenizer
:
StringTokenizer
は、互換性の理由で保持されているレガシークラスですが、新しいコードでは使用しないでください。この機能をお探しの方は、代わりにまたは
パッケージのsplit
メソッドを使用することをお勧めします。String
java.util.regex
問題はおそらくメソッドのパフォーマンスの低下であるString.split
ため、代替手段を見つける必要があります。
StringTokenizer
注:すべてのユースケースがこの方法よりも優れていると判断するのは難しいため、「パフォーマンスが低いと思われる」と言っていますString.split
。さらに、多くの場合、文字列のトークン化が実際に適切なプロファイリングによって決定されるアプリケーションのボトルネックでない限り、どちらかといえば時期尚早の最適化になると思います。最適化に取り掛かる前に、意味があり理解しやすいコードを書くと言いたくなります。
現在の要件からすると、おそらく独自のトークナイザーをローリングすることはそれほど難しくありません。
私たち自身のtokenzierを転がしてください!
以下は私が書いた単純なトークナイザーです。速度の最適化はなく、文字列の終わりを超えないようにするためのエラーチェックもありません。これは手っ取り早い実装です。
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で導入されたループ構造を実装Iterable
し、それIterator
を利用するために、であり、構造をサポートしていません。for-each
StringTokenizer
Enumerator
for-each
もっと速いですか?
これがもっと速いかどうかを調べるために、私は次の4つの方法で速度を比較するプログラムを作成しました。
- の使用
StringTokenizer
。
- 新しいの使用
MyTokenizer
。
- の使用
String.split
。
- によるプリコンパイルされた正規表現の使用
Pattern.compile
。
4つの方法では、文字列"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);
結果
テストはJavaSE6(ビルド1.6.0_12-b04)を使用して実行され、結果は次のとおりです。
実行1実行2実行3実行4実行5
----- ----- ----- ----- -----
StringTokenizer 172188187172172
MyTokenizer 234 234 235 234 235
String.split 1172 1156 1171 1172 1156
Pattern.compile 906 891 891 907 906
したがって、限られたテストとわずか5回の実行からStringTokenizer
わかるように、実際には最速でしたMyTokenizer
が、2位になりました。次に、String.split
が最も遅く、プリコンパイルされた正規表現はsplit
メソッドよりもわずかに高速でした。
他の小さなベンチマークと同様に、それはおそらく実際の状態をあまり表していないので、結果は塩の粒(またはマウンド)で取得する必要があります。