誰かが、StringBuffer
に_+
_演算子を使用するよりも、Javaの文字列を連結するためにString
を使用する方が効率的だと言った。あなたがそれをするとき、フードの下で何が起こりますか? StringBuffer
とはどう違うのですか?
最近ではほとんどすべての場合、StringBuilder(非同期バージョンですが、文字列を並行して作成するのはいつですか?)を使用することをお勧めしますが、次のようになります。
2つの文字列で+を使用すると、次のようなコードがコンパイルされます。
String third = first + second;
このようなものに:
StringBuilder builder = new StringBuilder( first );
builder.append( second );
third = builder.toString();
したがって、ほんの少しの例では、通常は違いはありません。ただし、複雑な文字列を作成する場合、これよりも多くの対処が必要になることがよくあります。たとえば、多くの異なる追加ステートメント、または次のようなループを使用している可能性があります。
for( String str : strings ) {
out += str;
}
この場合、新しいStringBuilder
インスタンスと新しいString
(out
-String
sの新しい値は不変です)が各反復で必要です。これは非常に無駄です。これを単一のStringBuilder
に置き換えると、単一のString
を生成でき、気にしないString
sでヒープを埋めることはできません。
次のような単純な連結の場合:
String s = "a" + "b" + "c";
jodonnellが指摘したように、StringBuffer
を使用するのはかなり無意味です:
String s = new StringBuffer().append("a").append("b").append("c").toString();
[〜#〜] but [〜#〜]ループ内で文字列を連結するのは、次のように非常に性能が劣ります。
String s = "";
for (int i = 0; i < 10; i++) {
s = s + Integer.toString(i);
}
このループで文字列を使用すると、メモリ内に10個の中間文字列オブジェクト「0」、「01」、「012」などが生成されます。 StringBuffer
を使用して同じ内容を記述している間に、単にStringBuffer
の内部バッファーを更新し、必要のない中間文字列オブジェクトを作成しません。
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 10; i++) {
sb.append(i);
}
実際、上記の例では、StringBuilder
の代わりにStringBuffer
(Java 1.5)で導入)を使用する必要があります-すべてのメソッドが同期されるため、StringBuffer
は少し重い。
一方が他方より高速であってはなりません。これは、Java 1.4.2の前には当てはまりませんでした。なぜなら、 "+"演算子を使用して3つ以上の文字列を連結すると、構築中にString
オブジェクトが作成されるからです。最後の文字列。
ただし、 StringBufferのJavaDoc が示すように、少なくともJava 1.4.2から "+"演算子を使用するとStringBuffer
が作成され、 append()
ingそれに多くの文字列。
ただし、ループ内で文字列を別の文字列に追加する場合は注意してください!例えば:
_String myString = "";
for (String s : listOfStrings) {
// Be careful! You're creating one intermediate String object
// for every iteration on the list (this is costly!)
myString += s;
}
_
ただし、通常、いくつかの文字列を「+」で連結すると、それらすべてをappend()
ingするよりもきれいになることに注意してください。
内部では、実際にStringBufferを作成して追加し、結果に対してtoString()を呼び出します。したがって、実際にどちらを使用するかは問題ではありません。
そう
String s = "a" + "b" + "c";
になる
String s = new StringBuffer().append("a").append("b").append("c").toString();
これは、単一のステートメント内の一連のインライン追加に当てはまります。複数のステートメントの過程で文字列を構築する場合、メモリを浪費しているため、StringBufferまたはStringBuilderの方が適しています。
Jdk1.5(またはそれ以降)および連結がスレッドセーフである場合、StringBufferの代わりにStringBuilderを使用する必要があります http://Java4ever.blogspot.com/2007/03/string-vs-stringbuffer-vs -stringbuilder.html 速度の向上に関して: http://www.about280.com/stringtest.html
個人的には読みやすさのためにコードを書くので、文字列の連結によってコードがかなり遅くなることが分からない限り、どちらの方法でもコードが読みやすくなるようにしてください。
場合によっては、コンパイラーによって実行される最適化のためにこれは時代遅れですが、一般的な問題は次のようなコードです:
string myString="";
for(int i=0;i<x;i++)
{
myString += "x";
}
以下のように動作します(各ステップは次のループの繰り返しです):
ご覧のとおり、各反復ではもう1文字をコピーする必要があるため、各ループで1 + 2 + 3 + 4 + 5 + ... + Nの操作が実行されます。これはO(n ^ 2)操作です。ただし、N文字しか必要ないことが事前にわかっている場合は、使用している文字列からN文字だけをコピーして、1回の割り当てでそれを行うことができます-単なるO(n)操作。
StringBuffer/StringBuilderは可変であるため、これを回避し、同じデータを繰り返しコピーする必要がないため(内部バッファーにコピーするスペースがある限り)。現在のサイズの割合でバッファを過剰に割り当てることにより、追加の数に比例した割り当てとコピーの実行を避け、償却O(1)追加を行います。
ただし、コンパイラは、コードをStringBuilderスタイルに最適化できることがよくあります(または、より良い-定数の折りたたみなどを実行できるため)。
知る限りでは、JVMのバージョンに依存します。1.5より前のバージョンでは、「+」または「+ =」を使用して、実際に文字列全体を毎回コピーしていました。
+ =を使用すると、実際に文字列の新しいコピーが割り当てられることに注意してください。
先ほど指摘したように、ループで+を使用すると、コピーが発生します。
連結された文字列がコンパイル時定数である場合、コンパイル時に連結されるため、
String foo = "a" + "b" + "c";
Hasは以下にコンパイルされます。
String foo = "abc";
Javaはstring1 + string2をStringBufferコンストラクト、append()、およびtoString()に変換します。意味あり。
ただし、Java 1.4以前では、ステートメント内のeach +演算子separatelyに対してこれを行います。これは、+ b + cは、two toString()呼び出しでtwo StringBufferコンストラクトになります。連結文字列が長い場合は、実際の混乱に変わります。これを制御して適切に実行できることを意味します。
Java 5.0以降は、より賢明にそれを行うように見えるため、問題は少なく、確かに冗長ではありません。
さらに詳しい情報:
StringBufferはスレッドセーフクラスです
public final class StringBuffer extends AbstractStringBuilder
implements Serializable, CharSequence
{
// .. skip ..
public synchronized StringBuffer append(StringBuffer stringbuffer)
{
super.append(stringbuffer);
return this;
}
// .. skip ..
}
ただし、StringBuilderはスレッドセーフではないため、可能であればStringBuilderを使用する方が高速です
public final class StringBuilder extends AbstractStringBuilder
implements Serializable, CharSequence
{
// .. skip ..
public StringBuilder append(String s)
{
super.append(s);
return this;
}
// .. skip ..
}
StringBufferは可変です。別のオブジェクトをインスタンス化せずに、文字列の値をsameオブジェクトに追加します。次のようなことをする:
myString = myString + "XYZ"
new Stringオブジェクトを作成します。
「+」を使用して2つの文字列を連結するには、新しい文字列に両方の文字列用のスペースを割り当ててから、両方の文字列からデータをコピーする必要があります。 StringBufferは連結用に最適化されており、最初に必要なスペースよりも多くのスペースを割り当てます。新しい文字列を連結すると、ほとんどの場合、既存の文字列バッファーの末尾に文字を簡単にコピーできます。
2つの文字列を連結する場合、「+」演算子はおそらくオーバーヘッドが少なくなりますが、連結する文字列が増えると、StringBufferが先に出て、メモリ割り当てが少なくなり、データのコピーが少なくなります。
StringBufferクラスは、連結する文字列の内容を保持する文字の配列を保持しますが、+メソッドは、呼び出されるたびに新しい文字列を作成し、2つのパラメーター(param1 + param2)を追加します。
StringBufferは、既存の配列を使用してすべての文字列を連結/保存できるため、より高速です。 2.配列に収まらない場合でも、各呼び出しに対して新しいStringオブジェクトを生成するよりも大きなバッキング配列を割り当てる方が高速です。
文字列は不変であるため、+演算子を呼び出すたびに新しいStringオブジェクトが作成され、新しいStringにStringデータがコピーされます。文字列のコピーには文字列の長さで線形の時間がかかるため、+演算子へのN呼び出しのシーケンスはO(N2)実行時間(2次)。
逆に、StringBufferは可変なので、Append()を実行するたびにStringをコピーする必要がないため、N個のAppend()呼び出しのシーケンスはO(N) time( linear)。これは、多数の文字列を一緒に追加する場合にのみ、ランタイムに大きな違いをもたらします。
前述のように、Stringオブジェクトは変更不可能です。つまり、一度作成すると(以下を参照)変更することはできません。
文字列x = new String( "something"); //または
文字列x = "何か";
したがって、Stringオブジェクトを連結しようとすると、それらのオブジェクトの値が取得され、新しいStringオブジェクトに入れられます。
代わりにStringBuffer(IS mutable)を使用する場合、値をchar(プリミティブ)の内部リストに継続的に追加します。これは、必要な値に合わせて拡張または切り捨てることができます。作成され、値を保持するために必要なときに新しい文字のみが作成/削除されます。
2つの文字列を連結すると、実際にはJavaで3番目のStringオブジェクトが作成されます。 StringBuffer(またはJava 5/6)のStringBuilderを使用)は、文字の内部配列を使用して文字列を格納し、add(...)メソッドの1つを使用するため、より高速です。 、新しいStringオブジェクトは作成されず、代わりにStringBuffer/Buiderが内部配列を追加します。
単純な連結では、StringBuffer/Builderまたは '+'演算子を使用して文字列を連結するかどうかは実際には問題ではありませんが、多くの文字列連結を行うと、StringBuffer/Builderを使用する方がはるかに高速であることがわかります。
Javaでは文字列は不変であるため、文字列を連結するたびに、新しいオブジェクトがメモリ内に作成されます。 StringBufferは、メモリ内の同じオブジェクトを使用します。
最も簡単な答えは、それが速いということです。
内部のすべてを本当に知りたい場合は、自分自身でソースを常に見ることができます。
Java Language Specificationの String Concatenation Operator + のセクションでは、+演算子が非常に遅い理由に関する背景情報を提供します。