回答:
彼らは本質的にコースの馬です。
Scanner
文字列を解析し、さまざまなタイプのデータを引き出す必要がある場合のために設計されています。これは非常に柔軟性がありますが、特定の式で区切られた文字列の配列を取得するための最も単純なAPIはおそらく提供しません。String.split()
そしてPattern.split()
あなたは後者を行うための簡単な構文を与えるが、それは本質的にすべての彼らが行うことです。結果の文字列を解析する場合、または特定のトークンに応じて区切り文字を途中で変更する場合は、それらは役に立ちません。StringTokenizer
は、よりもさらに制限が多くString.split()
、使用するのも少し手間がかかります。これは基本的に、固定部分文字列で区切られたトークンを引き出すために設計されています。この制限により、の約2倍の速度になりString.split()
ます。(私の参照の比較String.split()
とをStringTokenizer
。)また、正規表現のAPIを先行し、そのString.split()
一部です。典型的なマシンでは数ミリ秒で数千の文字列をString.split()
トークン化できる私のタイミングからわかるでしょう。さらに、出力が文字列配列として提供されるという利点があります。これは通常必要なものです。で提供されているを使用すると、ほとんどの場合「構文的に面倒」になります。この観点からすると、は最近のスペースの無駄遣いであり、だけを使用することもできます。StringTokenizer
Enumeration
StringTokenizer
StringTokenizer
String.split()
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()
です。
Scanner
は指定されたの改行文字を検出するために使用しましたString
。改行文字はプラットフォームによって異なり(Pattern
のjavadocを見てください)、入力文字列がに準拠することが保証されていないため、を呼び出すときに検索する改行文字がすでにわかっているSystem.lineSeparator()
ため、Scanner
より適切であることがわかりますnextLine()
。以下のためにString.split
私は任意の標準の場所(Iですから、それをコピーできる最善に保存されているが見つからない行区切り、検出するために、正しい正規表現パターンにフィードする必要がありますScanner
クラスのソースを)。
トークン化するStringオブジェクトがある場合は、StringTokenizerよりもStringのsplitメソッドの使用をお勧めします。ファイルやユーザーなど、プログラムの外部のソースからテキストデータを解析する場合は、スキャナーが役に立ちます。
スプリットは遅いですが、スキャナーほど遅くはありません。StringTokenizerは分割よりも高速です。ただし、JFastParser https://github.com/hughperkins/jfastparserで行ったスピードブーストを実現するために、ある程度の柔軟性を犠牲にすることで、2倍の速度を得ることができることがわかりました。
100万の倍精度を含む文字列でのテスト:
Scanner: 10642 ms
Split: 715 ms
StringTokenizer: 544ms
JFastParser: 290ms
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を操作する場合と比較して、プログラムの速度が低下する可能性があるということです。
デフォルトのシナリオでは、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つの重要な違いは、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のように動作させることができます。
String.split()は非常に機能しますが、独自の境界があります。たとえば、以下に示すように、単一または二重のパイプ(|)記号に基づいて文字列を分割したい場合、機能しません。この状況では、StringTokenizerを使用できます。
ABC | IJK