スキャナーvs. StringTokenizer vs. String.Split


155

私はちょうどJavaのスキャナークラスについて学んだ、今それがどのようにStringTokenizerとString.Splitと比較/競合するのか疑問に思っている。StringTokenizerとString.SplitはStringsでのみ機能することを知っているので、なぜStringに対してScannerを使用したいのですか?Scannerは分割のためのワンストップショッピングを意図したものですか?

回答:


240

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

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

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


8
String.SplitとStringTokenizerで実行したのと同じテストでScannerの結果を確認することも興味深いでしょう。
デイブ

2
「Java APIのメモに記載されているように、StringTokenizerの使用が推奨されないのはなぜですか?」という別の質問に対する答えを教えてください。このテキストから、答えは「String.split()が十分に速いため」であるようです。
レッグス

1
StringTokenizerは現在かなり廃止されていますか?
Steve the Maker

それの代わりに何を使うのですか?スキャナー?
エイドリアン

4
私はそれが古い質問に対する答えであることを理解していますが、巨大なテキストストリームをその場でトークンに分割する必要がある場合、メモリ不足になるStringTokenizerので、最善の策ではありませんString.split()か?
Sergei Tachenov 2016年

57

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

StringTokenizer新しいコードではその使用は推奨されませんが、互換性の理由で保持されるレガシークラスです。この機能を求める人は、代わりにsplitメソッドStringまたは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()です。


20
StringTokenizerは、String.split()の2倍の速さです。正規表現を使用する必要がない場合は、しないでください。
Alex Worden

Scannerは指定されたの改行文字を検出するために使用しましたString。改行文字はプラットフォームによって異なり(Patternのjavadocを見てください)入力文字列がに準拠することが保証されていないため、を呼び出すときに検索する改行文字がすでにわかっているSystem.lineSeparator()ため、Scannerより適切であることがわかりますnextLine()。以下のためにString.split私は任意の標準の場所(Iですから、それをコピーできる最善に保存されているが見つからない行区切り、検出するために、正しい正規表現パターンにフィードする必要がありますScannerクラスのソースを)。
ADTC 2013

9

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

splitはJDK 1.4で存在するようになりました。トークナイザーよりも低速ですが、Stringクラスから呼び出すことができるため、使いやすくなっています。

スキャナーはJDK 1.5上にあるようになりました。これは最も柔軟性があり、Java APIの長期にわたるギャップを埋め、有名なCs scanf関数ファミリーと同等のものをサポートします。


6

トークン化するStringオブジェクトがある場合は、StringTokenizerよりもStringのsplitメソッドの使用をお勧めします。ファイルやユーザーなど、プログラムの外部のソースからテキストデータを解析する場合は、スキャナーが役に立ちます。


5
そのように、正当化も理由もありませんか?
jan.supol 2016年

6

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

100万の倍精度を含む文字列でのテスト:

Scanner: 10642 ms
Split: 715 ms
StringTokenizer: 544ms
JFastParser: 290ms

一部のJavadocはすばらしいものでしたが、数値データ以外のものを解析したい場合はどうでしょうか。
NickJ 2013

まあ、それは美しさではなくスピードのために設計されています。これは非常にシンプルで、ほんの数行なので、必要に応じてテキスト解析のオプションをさらにいくつか追加できます。
ヒューパーキンス

4

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


3
この答えは少し無意味です。あなたは、より高速であるが「CPU負荷が少ない」ものを探していると言います。すべてのプログラムはCPUによって実行されます。プログラムがCPUを100%使用していない場合、プログラムはI / Oなどの他の何かを待機している必要があります。直接ディスクアクセスを行う場合(特にここでは行わない)を除いて、文字列のトークン化について説明するときには、これが問題になることはありません。
Jolta、2013年

4

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

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

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


4
実際、String.split()は常にパターンをコンパイルするとは限りません。1.7 Javaの場合はソースを見てください。パターンがエスケープされたものではなく単一の文字であるかどうかがチェックされ、正規表現なしで文字列が分割されるため、非常に高速です。
KrzysztofKrasoń12年

1

デフォルトのシナリオでは、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()を使用して、文字列の文字配列を取得します。例えば:

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

1

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のように動作させることができます。


-5

String.split()は非常に機能しますが、独自の境界があります。たとえば、以下に示すように、単一または二重のパイプ(|)記号に基づいて文字列を分割したい場合、機能しません。この状況では、StringTokenizerを使用できます。

ABC | IJK


12
実際、 "ABC | IJK" .split( "\\ |");だけで例を分割できます。
トモ

"ABC || DEF ||" .split( "\\ |")は、後続の2つの空の値を無視するため、実際には機能しません。これにより、構文解析が必要以上に複雑になります。
Armand 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.