質問

この, るという事実としていま億る文字列を解析した修正マコード受け入れ 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 つの方法で速度を比較するプログラムを作成しました。

  1. の使用 StringTokenizer.
  2. 新しいものの使用 MyTokenizer.
  3. の使用 String.split.
  4. によるプリコンパイル済み正規表現の使用 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"));
   }
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top