表現文字列になります。分割StringTokenizer
-
13-09-2019 - |
質問
励 この, るという事実としていま億る文字列を解析した修正マコード受け入れ StringTokenizer の代わりに String[]
も左んとから情報を入手できるようになっ美味しいx2性能を向上させはこだて
"dog,,cat".split(",")
//output: ["dog","","cat"]
StringTokenizer("dog,,cat")
// nextToken() = "dog"
// nextToken() = "cat"
どのように私達も、同様の結果、StringTokenizer?が速くなるのか?
解決
あなただけの、実際にカンマでトークン化されていますか?もしそうなら、私は自分のトークナイザを書きたい - それも、より効率的に複数のトークンを探すことができます、そしてあなたは、あなたがしたいしかし、それは動作させることができ、より汎用StringTokenizerはよりになってしまうことがあります。このような単純なユースケースでは、それは単純な実装とすることができる。
それは有用であろう場合は、、あなたもIterable<String>
を実装し、強い型付けの代わりEnumeration
によって提供さStringTokenizer
サポートを強化-forループのサポートを得ることができます。あなたは、このような獣をアップコーディング任意の助けをしたい場合は、私に教えてください - それは本当にあまりにも難しいことではありません。
また、私はあまりにも遠く、既存のソリューションから跳躍する前に、実際のデータにパフォーマンステストを実行してみたいです。あなたはは、実際にのString.split
に費やされているどのくらいのあなたの実行時間の任意のアイデアを持っていますか?私はあなたが解析する文字列をたくさん持っている知っていますが、その後彼らとの重要な何かをやっている場合、私はそれが分裂よりもはるかに重要であることを期待したい。
他のヒント
いろいろいじった後、 StringTokenizer
クラスを返すための要件を満たす方法が見つかりませんでした ["dog", "", "cat"]
.
さらに、 StringTokenizer
クラスは互換性の理由からのみ残されており、 String.split
奨励されています。API仕様より StringTokenizer
:
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
レベル。しかし、それは読者の練習問題として残しておきます。
クラスも実装します Iterable
そして Iterator
を活用するために for-each
Java 5 で導入されたループ構造。 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);
結果
テストは 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
僅差の2位となった。それから、 String.split
が最も遅く、プリコンパイルされた正規表現はそれよりわずかに高速でした。 split
方法。
他の小さなベンチマークと同様、これは実際の状況をあまり表していない可能性が高いため、結果は割り引いて理解する必要があります。
注意:そうするとそのベンチマークスキャナにすることに回程度以下の文字列になります。分割します。そのため、使用しない読み取ってくれます。
(これらのイ...続きを読の後に記録のことスキャナーが悪いことです。(として読み込み:なdownvoteく示唆するスキャナーをご---))
と仮定してご利用のJavaが1.5級以上のもの、 スキャナー, を実施する Iterator<String>
, どう:
Scanner sc = new Scanner("dog,,cat");
sc.useDelimiter(",");
while (sc.hasNext()) {
System.out.println(sc.next());
}
受けとる:
dog
cat
あなたがトークン化する必要がある文字列の種類に応じて、例えばString.indexOfに基づいて独自のスプリッタを()書きすることができます。また、文字列のトークン化は、互いに独立しているので、さらにパフォーマンスを向上させるために、マルチコアソリューションを作成することができます。コアあたり-lets say- 100個の文字列のバッチに取り組んでいます。 string.Split()または他のwateverを行います。
むしろStringTokenizerはより、あなたは私が引用のApache CommonsのラングからStrTokenizerクラスを試すことができます:
このクラスは、多くの小さな文字列に文字列を分割することができます。しかし、それは反復子インタフェースを実装するなど、より多くの制御と柔軟性を提供しています、StringTokenizerは同様の仕事をすることを目指しています。
空のトークンを除去またはヌルとして返されてもよいです。
これは何が必要、と思うように聞こえる?
あなたはそのような何かを行うことができます。これは完璧ではないが、それはあなたのために働く可能性があります。
public static List<String> find(String test, char c) {
List<String> list = new Vector<String>();
start;
int i=0;
while (i<=test.length()) {
int start = i;
while (i<test.length() && test.charAt(i)!=c) {
i++;
}
list.add(test.substring(start, i));
i++;
}
return list;
}
あなたがリストの事をOMMITと直接ストリングに何かを行うことができます可能であればます:
public static void split(String test, char c) {
int i=0;
while (i<=test.length()) {
int start = i;
while (i<test.length() && test.charAt(i)!=c) {
i++;
}
String s = test.substring(start,i);
// do something with the string here
i++;
}
}
私のシステムでは最後の方法は、StringTokenizerは、ソリューションよりも高速ですが、あなたはそれがあなたのためにどのように動作するかをテストすることをお勧めします。 (もちろん、あなたがommitingによって少し短く、このメソッドを作ることができる第二の{}外観はもちろん、あなたが使用することができながら、forループの代わりにアウターwhileループの、私はそれに++、私は」didnの最後を含みますtはそのここで私は悪いスタイルと考えているので、やるます。
さて、あなたができる最速のものは、例えば、手動で文字列を横断するだろう
List<String> split(String s) {
List<String> out= new ArrayList<String>();
int idx = 0;
int next = 0;
while ( (next = s.indexOf( ',', idx )) > -1 ) {
out.add( s.substring( idx, next ) );
idx = next + 1;
}
if ( idx < s.length() ) {
out.add( s.substring( idx ) );
}
return out;
}
この(非公式テスト)は、分割の2倍の速さのようなものになりそうです。しかし、それはエスケープコンマで解除されます例えば、この方法を反復するために少し危険だ、とあなたはいくつかの点でそれに対処する必要が終わるならば、時間によって(億文字列のリストを持っているので、図3は、カンマをエスケープ)します「それはあなたがおそらくスピード給付の一部を失ってしまいますのために許可されまします。
最終的にはそれはおそらく気に価値はありません。
私はGoogleのグアバSplitter
をお勧めします。
私はがcoobird のテストとそれを比較して得た結果を以下ます:
StringTokenizerは104
Googleのグアバスプリッタ142
string.Split 446
正規表現299
、あなたはJavaCCのコンパイラを見てすることができます。それはあなたの入力を読み込むJavaクラスを生成します。それは次のようになります:
TOKEN { <CAT: "cat"> , <DOG:"gog"> }
input: (cat() | dog())*
cat: <CAT>
{
animals.add(new Animal("Cat"));
}
dog: <DOG>
{
animals.add(new Animal("Dog"));
}