web-dev-qa-db-ja.com

スキャナーとStringTokenizerとString.Split

JavaのScannerクラスについて学びましたが、今ではStringTokenizerおよびString.Splitと比較/競合する方法について疑問に思っています。 StringTokenizerとString.Splitは文字列でのみ機能することを知っているので、文字列にScannerを使用する理由は何ですか? Scannerは、分割のためのワンストップショッピングを目的としていますか?

150
Dave

彼らは本質的にコースの馬です。

  • Scannerは、文字列を解析し、さまざまなタイプのデータを引き出す必要がある場合のために設計されています。これは非常に柔軟性がありますが、特定の式で区切られた文字列の配列を単純に取得するための最も単純なAPIはおそらく提供していません。
  • String.split()Pattern.split()は、後者を実行するための簡単な構文を提供しますが、基本的にはそれだけです。結果の文字列を解析したい場合、または特定のトークンに応じて区切り文字を途中で変更したい場合、それらはそれを助けません。
  • StringTokenizerString.split()よりもさらに制限的であり、使用するのが少し面倒です。基本的に、固定部分文字列で区切られたトークンを引き出すために設計されています。この制限のため、String.split()の約2倍の速度です。 (私の String.split()StringTokenizerの比較 をご覧ください。)また、String.split()がその一部である正規表現APIよりも前のものです。

私のタイミングから、String.split()は通常のマシンでは数ミリ秒で数千の文字列をトークン化できることに注意してください。さらに、StringTokenizerに比べて、出力を文字列配列として提供するという利点があります。これは通常、必要なものです。 Enumerationで提供されるStringTokenizerの使用は、ほとんどの場合「構文的に面倒」です。この観点から、StringTokenizerは最近のスペースの無駄であり、String.split()を使用することもできます。

236
Neil Coffey

StringTokenizer を削除することから始めましょう。古くなっており、正規表現もサポートしていません。そのドキュメントの状態:

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

すぐに捨てましょう。 split()Scanner が残ります。それらの違いは何ですか?

一つには、split()は単に配列を返すだけなので、foreachループを簡単に使用できます。

for (String token : input.split("\\s+") { ... }

Scannerは、ストリームのように構築されます。

while (myScanner.hasNext()) {
    String token = myScanner.next();
    ...
}

または

while (myScanner.hasNextDouble()) {
    double token = myScanner.nextDouble();
    ...
}

(かなり 大規模なAPI があるため、常にこのような単純なものに制限されているとは思わないでください。)

このストリームスタイルのインターフェイスは、解析を開始する前にすべての入力を取得できない(または取得できない)場合に、単純なテキストファイルまたはコンソール入力の解析に役立ちます。

個人的には、Scannerを使用したことを覚えているのは、コマンドラインからユーザー入力を取得する必要があった学校プロジェクトのときだけです。そのような操作が簡単になります。ただし、分割したいStringがある場合、split()を使用するのは簡単です。

57
Michael Myers

StringTokenizerは常にそこにありました。それはすべての最速ですが、列挙型のイディオムは他のイディオムほどエレガントに見えないかもしれません。

jDK 1.4でsplitが存在するようになりました。トークナイザーよりも遅いですが、Stringクラスから呼び出し可能なため、使いやすいです。

スキャナーがJDK 1.5に搭載されました。最も柔軟性があり、Java AP​​Iの長年のギャップを埋めて、有名なCs scanf関数ファミリーに相当するものをサポートします。

9

スプリットは遅いですが、スキャナーほど遅くはありません。 StringTokenizerは分割よりも高速です。しかし、JFastParserで行ったスピードブーストを得るために、ある程度の柔軟性を交換することで2倍の速度を得ることができることがわかりました https://github.com/hughperkins/jfastparser

100万個のdoubleを含む文字列のテスト:

Scanner: 10642 ms
Split: 715 ms
StringTokenizer: 544ms
JFastParser: 290ms
6
Hugh Perkins

トークン化するStringオブジェクトがある場合は、StringTokenizerよりもStringの split メソッドを使用してください。ファイルやユーザーなど、プログラムの外部のソースからテキストデータを解析する場合は、スキャナーが便利です。

5
Bill the Lizard

String.splitは、StringTokenizerよりもかなり遅いようです。分割の唯一の利点は、トークンの配列を取得できることです。また、任意の正規表現を分割して使用できます。 org.Apache.commons.lang.StringUtilsには、2つのvizのいずれよりもはるかに高速に動作するsplitメソッドがあります。 StringTokenizerまたはString.split。ただし、3つすべてのCPU使用率はほぼ同じです。そのため、CPUをあまり使用しないメソッドも必要ですが、それを見つけることはできません。

4
Manish

私は最近、パフォーマンスに非常に敏感な状況でのString.split()の悪いパフォーマンスについていくつかの実験を行いました。これは便利かもしれません。

http://eblog.chrononsystems.com/hidden-evils-of-javas-stringsplit-and-stringr

要点は、String.split()が毎回正規表現パターンをコンパイルするため、プリコンパイルされたPatternオブジェクトを使用して直接Stringを操作する場合と比較して、プログラムが遅くなる可能性があることです。

4
pdeva

1つの重要な違いは、String.split()とScannerの両方が空の文字列を生成できるが、StringTokenizerがそれを実行しないことです。

例えば:

String str = "ab cd  ef";

StringTokenizer st = new StringTokenizer(str, " ");
for (int i = 0; st.hasMoreTokens(); i++) System.out.println("#" + i + ": " + st.nextToken());

String[] split = str.split(" ");
for (int i = 0; i < split.length; i++) System.out.println("#" + i + ": " + split[i]);

Scanner sc = new Scanner(str).useDelimiter(" ");
for (int i = 0; sc.hasNext(); i++) System.out.println("#" + i + ": " + sc.next());

出力:

//StringTokenizer
#0: ab
#1: cd
#2: ef
//String.split()
#0: ab
#1: cd
#2: 
#3: ef
//Scanner
#0: ab
#1: cd
#2: 
#3: ef

これは、String.split()およびScanner.useDelimiter()の区切り文字が単なる文字列ではなく、正規表現であるためです。上記の例の区切り文字「」を「+」に置き換えて、StringTokenizerのように動作させることができます。

1
John29

デフォルトのシナリオでは、Pattern.split()も推奨しますが、最大のパフォーマンスが必要な場合(特にAndroidでテストしたすべてのソリューションは非常に遅い)、1つの文字で分割するだけで済みます。今、私自身の方法を使用します:

public static ArrayList<String> splitBySingleChar(final char[] s,
        final char splitChar) {
    final ArrayList<String> result = new ArrayList<String>();
    final int length = s.length;
    int offset = 0;
    int count = 0;
    for (int i = 0; i < length; i++) {
        if (s[i] == splitChar) {
            if (count > 0) {
                result.add(new String(s, offset, count));
            }
            offset = i + 1;
            count = 0;
        } else {
            count++;
        }
    }
    if (count > 0) {
        result.add(new String(s, offset, count));
    }
    return result;
}

「abc」.toCharArray()を使用して、ストリングのchar配列を取得します。例えば:

String s = "     a bb   ccc  dddd eeeee  ffffff    ggggggg ";
ArrayList<String> result = splitBySingleChar(s.toCharArray(), ' ');
1
Simon Heinen