スキャナー対stringtokenizer vs. string.split
-
22-08-2019 - |
質問
Javaのスキャナークラスについて学んだばかりで、今ではStringTokenizerとString.splitとどのように比較/競合するのか疑問に思っています。 stringtokenizerとstring.splitは文字列でのみ動作することを知っているので、なぜスキャナーを文字列に使用したいのですか?スキャナーは、分割するためにワンストップショッピングをすることを意図していますか?
解決
彼らは本質的にコースの馬です。
Scanner
文字列を解析する必要がある場合に設計されており、さまざまなタイプのデータを引き出します。非常に柔軟ですが、特定の式で区切られた文字列の配列を単純に取得するための最も単純なAPIを提供することは間違いありません。String.split()
とPattern.split()
後者を行うための簡単な構文を提供しますが、それは本質的に彼らがするすべてのことです。結果の文字列を解析したり、特定のトークンに応じて途中で区切り文字を変更したりする場合は、それを助けません。StringTokenizer
さらに制限がありますString.split()
, また、使用するのに少し気分が悪くなります。固定サブストリングによって区切られたトークンを引き出すために基本的に設計されています。この制限のため、それはString.split()
. 。 (私のを参照してください の比較String.split()
とStringTokenizer
。)それはまた、通常の式のAPIよりも前のものです。String.split()
一部です。
あなたは私のタイミングからそれに注意します 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でスプリットが存在しました。トークン剤よりも遅いですが、文字列クラスから呼び出すことができるため、使いやすいです。
スキャナーはJDK 1.5に入るようになりました。これは最も柔軟であり、Java APIの長年のギャップを埋めて、有名なCS SCANF機能ファミリに相当するものをサポートしています。
スプリットは遅いですが、スキャナーほど遅くはありません。 StringTokenizerは、分割よりも速いです。しかし、JfastParserで行った速度を取得するために、ある程度の柔軟性を取引することで、速度を2倍に取得できることがわかりました。 https://github.com/hughperkins/jfastparser
100万倍のダブルを含む文字列でのテスト:
Scanner: 10642 ms
Split: 715 ms
StringTokenizer: 544ms
JFastParser: 290ms
トークン化する文字列オブジェクトがある場合は、文字列を使用することを支持します スプリット StringTokenizerのメソッド。ファイルから、またはユーザーのように、プログラムの外側のソースからテキストデータを解析する場合、スキャナーが役立ちます。
string.splitは、stringtokenizerよりもはるかに遅いようです。スプリットの唯一の利点は、トークンの配列を取得することです。また、スプリットで任意の正規表現を使用することもできます。 org.apache.commons.lang.stringutilsには、2つのvizのいずれよりもはるかに速く動作する分割方法があります。 stringtokenizerまたはstring.split。しかし、3つすべてのCPU使用率はほぼ同じです。そのため、CPU集約型の少ない方法も必要です。これはまだ見つかりません。
私は最近、非常にパフォーマンスに敏感な状況でstring.split()の悪いパフォーマンスについていくつかの実験を行いました。これが便利だと思うかもしれません。
http://eblog.chrononsystems.com/hidden-evils-of-javas-stringsplit-and-stringr
要点は、string.split()が毎回正規表現パターンをコンパイルしているため、事前コンパイルされたパターンオブジェクトを使用し、直接使用して文字列で動作する場合と比較して、プログラムを遅くすることができます。
デフォルトのシナリオについては、pattern.split()もお勧めしますが、最大のパフォーマンスが必要な場合(特にAndroidでテストしたすべてのソリューションは非常に遅くなります)、単一のcharで分割するだけで、独自の方法を使用する必要があります。
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つは、string.split()とスキャナーの両方が空の文字列を生成できることですが、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