JavaのScannerクラスについて学びましたが、今ではStringTokenizerおよびString.Splitと比較/競合する方法について疑問に思っています。 StringTokenizerとString.Splitは文字列でのみ機能することを知っているので、文字列にScannerを使用する理由は何ですか? Scannerは、分割のためのワンストップショッピングを目的としていますか?
彼らは本質的にコースの馬です。
Scanner
は、文字列を解析し、さまざまなタイプのデータを引き出す必要がある場合のために設計されています。これは非常に柔軟性がありますが、特定の式で区切られた文字列の配列を単純に取得するための最も単純なAPIはおそらく提供していません。String.split()
とPattern.split()
は、後者を実行するための簡単な構文を提供しますが、基本的にはそれだけです。結果の文字列を解析したい場合、または特定のトークンに応じて区切り文字を途中で変更したい場合、それらはそれを助けません。StringTokenizer
はString.split()
よりもさらに制限的であり、使用するのが少し面倒です。基本的に、固定部分文字列で区切られたトークンを引き出すために設計されています。この制限のため、String.split()
の約2倍の速度です。 (私の String.split()
とStringTokenizer
の比較 をご覧ください。)また、String.split()
がその一部である正規表現APIよりも前のものです。私のタイミングから、String.split()
は通常のマシンでは数ミリ秒で数千の文字列をトークン化できることに注意してください。さらに、StringTokenizer
に比べて、出力を文字列配列として提供するという利点があります。これは通常、必要なものです。 Enumeration
で提供されるStringTokenizer
の使用は、ほとんどの場合「構文的に面倒」です。この観点から、StringTokenizer
は最近のスペースの無駄であり、String.split()
を使用することもできます。
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()
を使用するのは簡単です。
StringTokenizerは常にそこにありました。それはすべての最速ですが、列挙型のイディオムは他のイディオムほどエレガントに見えないかもしれません。
jDK 1.4でsplitが存在するようになりました。トークナイザーよりも遅いですが、Stringクラスから呼び出し可能なため、使いやすいです。
スキャナーがJDK 1.5に搭載されました。最も柔軟性があり、Java APIの長年のギャップを埋めて、有名なCs scanf関数ファミリーに相当するものをサポートします。
スプリットは遅いですが、スキャナーほど遅くはありません。 StringTokenizerは分割よりも高速です。しかし、JFastParserで行ったスピードブーストを得るために、ある程度の柔軟性を交換することで2倍の速度を得ることができることがわかりました https://github.com/hughperkins/jfastparser
100万個のdoubleを含む文字列のテスト:
Scanner: 10642 ms
Split: 715 ms
StringTokenizer: 544ms
JFastParser: 290ms
トークン化するStringオブジェクトがある場合は、StringTokenizerよりもStringの split メソッドを使用してください。ファイルやユーザーなど、プログラムの外部のソースからテキストデータを解析する場合は、スキャナーが便利です。
String.splitは、StringTokenizerよりもかなり遅いようです。分割の唯一の利点は、トークンの配列を取得できることです。また、任意の正規表現を分割して使用できます。 org.Apache.commons.lang.StringUtilsには、2つのvizのいずれよりもはるかに高速に動作するsplitメソッドがあります。 StringTokenizerまたはString.split。ただし、3つすべてのCPU使用率はほぼ同じです。そのため、CPUをあまり使用しないメソッドも必要ですが、それを見つけることはできません。
私は最近、パフォーマンスに非常に敏感な状況でのString.split()の悪いパフォーマンスについていくつかの実験を行いました。これは便利かもしれません。
http://eblog.chrononsystems.com/hidden-evils-of-javas-stringsplit-and-stringr
要点は、String.split()が毎回正規表現パターンをコンパイルするため、プリコンパイルされたPatternオブジェクトを使用して直接Stringを操作する場合と比較して、プログラムが遅くなる可能性があることです。
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のように動作させることができます。
デフォルトのシナリオでは、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(), ' ');