40

私のソフトウェアでは、文字列を単語に分割する必要があります。私は現在、それぞれ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++)
4

10 に答える 10

63

データがすでにデータベースにある場合は、単語の文字列を解析する必要がある場合は、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時間かかります。split vs StringTokenizerを使用するコストは、それぞれ0.01ミリ秒よりはるかに少ないです。1,900万x30ワードを解析するには*ワードあたり8文字は約10秒かかります(2秒あたり約1 GB)

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

于 2011-05-11T14:22:56.933 に答える
14

Java 7でのSplitは、この入力に対してindexOfを呼び出すだけです。ソースを参照してください。分割は非常に高速で、indexOfの繰り返し呼び出しに近い必要があります。

于 2012-06-21T22:33:19.177 に答える
6

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

于 2011-05-11T14:27:13.010 に答える
5

もう1つの重要なことは、私が気付いた限りでは文書化されていませんが、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%高速です。

于 2013-08-29T01:38:02.453 に答える
3

分割を使用します。

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

于 2011-05-11T14:22:46.650 に答える
2

レガシーステータスに関係なく、正規表現を使用しないため、このタスクStringTokenizerよりも大幅に高速になると思います。を介して行うのと同じように、入力を直接スキャンするだけです。実際、正規表現を呼び出すたびにコンパイルする必要があるため、正規表現を直接使用するほど効率的ではありません。String.split()indexOf()String.split()

于 2011-05-12T00:09:23.560 に答える
2

19,000,000のドキュメントはそこで何をしなければなりませんか?すべてのドキュメントで定期的に単語を分割する必要がありますか?それともワンシュートの問題ですか?

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

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

于 2011-05-11T14:27:11.967 に答える
2

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

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

于 2013-08-30T08:13:09.233 に答える
1

これは、1.6.0を使用した妥当なベンチマークになる可能性があります

http://www.javamex.com/tutorials/regular_expressions/splitting_tokenisation_performance.shtml#.V6-CZvnhCM8
于 2016-08-13T20:26:59.130 に答える
-1

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

ここに画像の説明を入力してください

しかし、Javaのドキュメントによると、その使用は推奨されていません。こちらをチェック

于 2017-07-07T12:41:21.667 に答える