web-dev-qa-db-ja.com

JavaでのStringTokenizerクラスとString.splitメソッドのパフォーマンス

私のソフトウェアでは、文字列を単語に分割する必要があります。現在、30語以上のドキュメントが19,000,000以上あります。

次の2つの方法のうち、これを行う最良の方法はどれですか(パフォーマンスの観点から)。

StringTokenizer sTokenize = new StringTokenizer(s," ");
while (sTokenize.hasMoreTokens()) {

または

String[] splitS = s.split(" ");
for(int i =0; i < splitS.length; i++)
37
JohnJohnGa

データがすでにデータベースにある場合、単語の文字列を解析する必要がある場合は、indexOfを繰り返し使用することをお勧めします。どちらのソリューションよりも何倍も高速です。

ただし、データベースからデータを取得することは、依然としてはるかに高価になる可能性があります。

StringBuilder sb = new StringBuilder();
for (int i = 100000; i < 100000 + 60; i++)
    sb.append(i).append(' ');
String sample = sb.toString();

int runs = 100000;
for (int i = 0; i < 5; i++) {
    {
        long start = System.nanoTime();
        for (int r = 0; r < runs; r++) {
            StringTokenizer st = new StringTokenizer(sample);
            List<String> list = new ArrayList<String>();
            while (st.hasMoreTokens())
                list.add(st.nextToken());
        }
        long time = System.nanoTime() - start;
        System.out.printf("StringTokenizer took an average of %.1f us%n", time / runs / 1000.0);
    }
    {
        long start = System.nanoTime();
        Pattern spacePattern = Pattern.compile(" ");
        for (int r = 0; r < runs; r++) {
            List<String> list = Arrays.asList(spacePattern.split(sample, 0));
        }
        long time = System.nanoTime() - start;
        System.out.printf("Pattern.split took an average of %.1f us%n", time / runs / 1000.0);
    }
    {
        long start = System.nanoTime();
        for (int r = 0; r < runs; r++) {
            List<String> list = new ArrayList<String>();
            int pos = 0, end;
            while ((end = sample.indexOf(' ', pos)) >= 0) {
                list.add(sample.substring(pos, end));
                pos = end + 1;
            }
        }
        long time = System.nanoTime() - start;
        System.out.printf("indexOf loop took an average of %.1f us%n", time / runs / 1000.0);
    }
 }

プリント

StringTokenizer took an average of 5.8 us
Pattern.split took an average of 4.8 us
indexOf loop took an average of 1.8 us
StringTokenizer took an average of 4.9 us
Pattern.split took an average of 3.7 us
indexOf loop took an average of 1.7 us
StringTokenizer took an average of 5.2 us
Pattern.split took an average of 3.9 us
indexOf loop took an average of 1.8 us
StringTokenizer took an average of 5.1 us
Pattern.split took an average of 4.1 us
indexOf loop took an average of 1.6 us
StringTokenizer took an average of 5.0 us
Pattern.split took an average of 3.8 us
indexOf loop took an average of 1.6 us

ファイルを開くコストは約8ミリ秒です。ファイルが非常に小さいため、キャッシュを使用するとパフォーマンスが2〜5倍向上する場合があります。それでも、ファイルを開くのに約10時間かかります。スプリットとStringTokenizerを使用するコストは、それぞれ0.01ミリ秒未満です。 1900万x 30ワード* 1ワードあたり8文字を解析するには、約10秒かかります(2秒あたり約1 GB)。

パフォーマンスを改善したい場合は、はるかに少ないファイルをお勧めします。例えばデータベースを使用します。 SQLデータベースを使用したくない場合は、これらのいずれかを使用することをお勧めします http://nosql-database.org/

63
Peter Lawrey

Split in Java 7は、この入力に対してindexOfを呼び出すだけです ソースを参照 。分割は、indexOfの繰り返し呼び出しに近い、非常に高速でなければなりません。

14
nes1983

Java API仕様では、splitの使用が推奨されています。 StringTokenizerのドキュメント を参照してください。

6
developer

私が気づいた限り文書化されていない別の重要なことは、StringTokenizerにトークン化された文字列とともに区切り文字を返すように要求することです(コンストラクタStringTokenizer(String str, String delim, boolean returnDelims)を使用して)処理時間も短縮されます。したがって、パフォーマンスを探している場合は、次のようなものを使用することをお勧めします。

private static final String DELIM = "#";

public void splitIt(String input) {
    StringTokenizer st = new StringTokenizer(input, DELIM, true);
    while (st.hasMoreTokens()) {
        String next = getNext(st);
        System.out.println(next);
    }
}

private String getNext(StringTokenizer st){  
    String value = st.nextToken();
    if (DELIM.equals(value))  
        value = null;  
    else if (st.hasMoreTokens())  
        st.nextToken();  
    return value;  
}

GetNext()メソッドによって導入されたオーバーヘッドにも関わらず、区切り文字が破棄されますが、ベンチマークによると、それでも50%高速です。

4
cristianoms

分割を使用します。

StringTokenizerは、互換性のために保持されているレガシークラスですが、新しいコードでは使用しないでください。この機能を探している人は、代わりにsplitメソッドを使用することをお勧めします。

4
Basanth Roy

レガシーステータスに関係なく、正規表現を使用しないため、このタスクではStringTokenizerString.split()よりも大幅に高速であると予想されます。 indexOf()を介して自分自身になります。実際、String.split()は、呼び出すたびに正規表現をcompileする必要があるため、正規表現を直接使用するほど効率的ではありません。

2
user207421

そこで19,000,000件のドキュメントで何をする必要がありますか?すべてのドキュメントの単語を定期的に分割する必要がありますか?それとも、それは一発の問題ですか?

30個のWordで一度に1つのドキュメントを表示/要求する場合、これは非常に小さな問題であり、どの方法でも機能します。

一度にすべてのドキュメントを30語だけで処理する必要がある場合、これは非常に小さな問題であるため、IOとにかくバインドされる可能性が高くなります。

2

マイクロ(この場合はナノ)ベンチマークを実行している間、結果に影響する多くの要素があります。ほんの数例を挙げると、JIT最適化とガベージコレクション。

マイクロベンチマークから有意義な結果を得るには、 jmh ライブラリを確認してください。優れたベンチマークの実行方法に関する優れたサンプルがバンドルされています。

2
Ivo

パフォーマンスに関しては、StringTokeniserは分割よりもはるかに優れています。以下のコードを確認し、

enter image description here

しかし、Java docsによると、その使用は推奨されていません。チェック ここ

1
Code_Mode

これは、1.6.0を使用した合理的なベンチマークです。

http://www.javamex.com/tutorials/regular_expressions/splitting_tokenisation_performance.shtml#.V6-CZvnhCM8
1
chiperortiz