回答:
変異性の違い:
String
は不変です。値を変更しようとすると、別のオブジェクトが作成されますが、StringBuffer
およびStringBuilder
は変更可能であるため、値を変更できます。
スレッドセーフの違い:
違いStringBuffer
とはStringBuilder
つまりStringBuffer
、スレッドセーフです。したがって、アプリケーションを単一スレッドでのみ実行する必要がある場合は、を使用することをお勧めしますStringBuilder
。StringBuilder
よりも効率的ですStringBuffer
。
状況:
String
オブジェクトが不変であるため、Stringクラスを使用します。StringBuilder
だけで十分です。StringBuffer
ためにStringBuffer
が同期であるので、を使用します。Strings
、値を変更すると、別のオブジェクトが作成されます。古いオブジェクト参照は無効化されているので、によってガベージコレクションされる可能性がありますGC
か、それともガベージコレクションされますか?
String
してStringBuilder
?
String
不変構造が適切な場合に使用します。から新しい文字シーケンスを取得するとString
、CPU時間またはメモリのいずれかで、許容できないパフォーマンスペナルティが発生する可能性があります(データがコピーされないため、部分文字列の取得はCPU効率が良いですが、これは、はるかに大量のデータが割り当てられたままになる可能性があることを意味します)。StringBuilder
変更可能な文字シーケンスを作成する必要がある場合に使用します。通常、複数の文字シーケンスを連結します。StringBuffer
するのと同じ状況で使用しますがStringBuilder
、基になる文字列への変更を同期する必要がある場合(複数のスレッドが文字列バッファーを読み取り/変更しているため)。こちらの例をご覧ください。
基礎:
String
不変クラスであり、変更できません。
StringBuilder
に追加、置換、または削除でき、最終的にに変換できる変更可能なクラスString
StringBuffer
です。StringBuilder
StringBuilder
オブジェクトにアクセスするスレッドが1つしかないすべての場合を優先する必要があります。
詳細:
また、これStringBuilder/Buffers
は魔法ではなく、配列をバッキングオブジェクトとして使用するだけであり、配列がいっぱいになると再割り当てする必要があることにも注意してください。呼び出されるStringBuilder/Buffer
たびに常にサイズを変更する必要がない場所に、十分に大きなオブジェクトを作成してください.append()
。
サイズ変更は非常に退化する可能性があります。基本的に、拡張する必要があるたびに、バッキング配列のサイズを現在のサイズの2倍に変更します。これにより、StringBuilder/Buffer
クラスが大きくなり始めたときに大量のRAMが割り当てられ、使用されなくなる可能性があります。
Java String x = "A" + "B";
ではStringBuilder
舞台裏を使用します。したがって、単純なケースでは、自分で宣言するメリットはありません。ただしString
、4k未満などの大きなオブジェクトを構築する場合、宣言StringBuilder sb = StringBuilder(4096);
は、連結や16文字のみのデフォルトのコンストラクタを使用するよりもはるかに効率的です。あなたString
が10k未満になる場合は、安全のためにコンストラクタで10kに初期化してください。しかし、それが10kに初期化されている場合は、10kを超える1文字を書き込むと、再割り当てされて20k配列にコピーされます。したがって、高に初期化する方が低に初期化するよりも優れています。
自動サイズ変更の場合、17番目の文字でバッキング配列が再割り当てされて32文字にコピーされます。33番目の文字でこれが再び発生し、配列が再割り当てされて64文字にコピーされます。これが、多くの再割り当てとコピーにどのように縮退するかを確認できます。これはStringBuilder/Buffer
、そもそも使用を避けようとしているものです。
これは、AbstractStringBuilderのJDK 6ソースコードからのものです。
void expandCapacity(int minimumCapacity) {
int newCapacity = (value.length + 1) * 2;
if (newCapacity < 0) {
newCapacity = Integer.MAX_VALUE;
} else if (minimumCapacity > newCapacity) {
newCapacity = minimumCapacity;
}
value = Arrays.copyOf(value, newCapacity);
}
ベストプラクティスは、StringBuilder/Buffer
その大きさがどれほど大きいかわからない場合でも、必要になると思われるよりも少し大きい値を初期化String
することですが、推測することはできます。必要以上にわずかに多くのメモリを割り当てる方が、多くの再割り当てやコピーよりも優れています。
また、文字列+ 16文字のサイズのみが割り当てられるasを使用StringBuilder/Buffer
して初期化すると、String
ほとんどの場合、回避しようとしている縮退した再割り当てとコピーサイクルが開始されます。以下は、Java 6ソースコードからの抜粋です。
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
StringBuilder/Buffer
作成したものではなく、呼び出されたコンストラクターを制御できないインスタンスが偶然に発生する場合は、縮退した再割り当てとコピーの動作を回避する方法があります。.ensureCapacity()
結果String
が収まるようにしたいサイズで呼び出します。
代替案:
ちょうど注意として、もしあなたが本当に重い String
構築と操作をしているなら、ロープと呼ばれるはるかにパフォーマンス指向の代替があります。
別の方法として、をStringList
サブクラス化して実装を作成し、ArrayList<String>
カウンターを追加して.append()
、リストのあらゆる変異操作の文字数を追跡し、オーバーライド.toString()
して、StringBuilder
必要な正確なサイズのを作成し、リストをループしてビルドします出力ではStringBuilder
、インスタンス変数を作成してその結果を「キャッシュ」し、.toString()
何かが変更されたときにのみ再生成する必要があります。
またString.format()
、固定形式の出力を作成する場合も忘れないでください。これらは、コンパイラーによって改善されるため、コンパイラーによって最適化できます。
String x = "A" + "B";
本当にStringBuilderのようにコンパイル?なぜにコンパイルしないのですか?String x = "AB";
コンポーネントがコンパイル時に不明な場合にのみ、StringBuilderを使用する必要があります。
連結するということですか?
実際の例: 他の多くの文字列から新しい文字列を作成したいとします。
たとえば、メッセージを送信するには:
ストリング
String s = "Dear " + user.name + "<br>" +
" I saw your profile and got interested in you.<br>" +
" I'm " + user.age + "yrs. old too"
StringBuilder
String s = new StringBuilder().append.("Dear ").append( user.name ).append( "<br>" )
.append(" I saw your profile and got interested in you.<br>")
.append(" I'm " ).append( user.age ).append( "yrs. old too")
.toString()
または
String s = new StringBuilder(100).appe..... etc. ...
// The difference is a size of 100 will be allocated upfront as fuzzy lollipop points out.
StringBuffer(構文はStringBuilderとまったく同じで、効果は異なります)
約
StringBuffer
対 StringBuilder
前者は同期され、後で同期されません。
したがって、単一のスレッドでそれを数回呼び出すと(ケースの90%です)、スレッドロックを所有しているかどうかを確認するために停止しないため、 StringBuilder
実行速度が大幅に向上します。
したがって、使用することをお勧めしますStringBuilder
(もちろん、同時に複数のスレッドがアクセスしている場合を除き、これはまれです)
String
連結(+演算子を使用)はコンパイラによって最適化され、StringBuilder
その下で使用されるため、心配する必要はありません。Javaの古くから、これはすべての連結が原因であるため、誰もが絶対に避けるべきであると誰もが言っていることでした。新しいStringオブジェクトを作成しました。最近のコンパイラーはこれを行わなくなりましたがStringBuilder
、「古い」コンパイラーを使用する場合に備えて、代わりに使用することをお勧めします。
編集する
好奇心の強い人のために、これはコンパイラがこのクラスに対して行うことです:
class StringConcatenation {
int x;
String literal = "Value is" + x;
String builder = new StringBuilder().append("Value is").append(x).toString();
}
javap -c StringConcatenation
Compiled from "StringConcatenation.java"
class StringConcatenation extends java.lang.Object{
int x;
java.lang.String literal;
java.lang.String builder;
StringConcatenation();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: aload_0
5: new #2; //class java/lang/StringBuilder
8: dup
9: invokespecial #3; //Method java/lang/StringBuilder."<init>":()V
12: ldc #4; //String Value is
14: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
17: aload_0
18: getfield #6; //Field x:I
21: invokevirtual #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
24: invokevirtual #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
27: putfield #9; //Field literal:Ljava/lang/String;
30: aload_0
31: new #2; //class java/lang/StringBuilder
34: dup
35: invokespecial #3; //Method java/lang/StringBuilder."<init>":()V
38: ldc #4; //String Value is
40: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
43: aload_0
44: getfield #6; //Field x:I
47: invokevirtual #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
50: invokevirtual #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
53: putfield #10; //Field builder:Ljava/lang/String;
56: return
}
5から27の番号が付けられた行は、「literal」という名前のストリング用です
31から53までの番号が付けられた行は、「ビルダー」という名前の文字列用です。
違いはありません。両方の文字列に対してまったく同じコードが実行されます。
StringBuilder
割り当ての右側でaを使用して文字列連結を行うことは、一般的には良い習慣ではありません。良い実装は、あなたが言うように裏でStringBuilderを使用します。さらに、の例は"a" + "b"
1つのリテラルにコンパイルされ"ab"
ますが、それを使用StringBuilder
すると、2つの不要な呼び出しが発生しappend()
ます。
"a"+"b"
たが、文字列の連結とは何かを明示的に変更しました。あなたが言わないことは、なぜそれを行うのは良い習慣ではないということです。これがまさに(最新の)コンパイラが行うことです。@fuzzy、私は同意します。特に、最終的な文字列のサイズが(aprox)になるとわかっている場合はそうです。
-------------------------------------------------- -------------------------------- String StringBuffer StringBuilder -------------------------------------------------- -------------------------------- 保管エリア| 定数文字列プールヒープヒープ 変更可能| いいえ(不変)はい(変更可能)はい(変更可能) スレッドセーフ| はいはいいいえ パフォーマンス| 速いとても遅い速い -------------------------------------------------- --------------------------------
synchronised
、それが理由です。
ストリング
String class
文字列を表します。など、Javaプログラムのすべての文字列リテラルは"abc"
、このクラスのインスタンスとして実装されます。
Stringオブジェクトは不変彼らは我々が変更することはできません作成された後。(文字列は定数です)
文字列がコンストラクタまたはメソッドを使用して作成された場合、それらの文字列はと同様にヒープメモリに格納されSringConstantPool
ます。ただし、プールに保存する前に、intern()
equalsメソッドを使用して、プール内の同じコンテンツでオブジェクトの可用性を確認するメソッドを呼び出します。String-copyがプールで利用可能な場合、参照を返します。それ以外の場合、Stringオブジェクトがプールに追加され、参照が返されます。
+
)、および他のオブジェクトから文字列への変換を特別にサポートしています。文字列の連結は、StringBuilder(またはStringBuffer)クラスとその追加メソッドによって実装されます。String heapSCP = new String("Yash");
heapSCP.concat(".");
heapSCP = heapSCP + "M";
heapSCP = heapSCP + 777;
// For Example: String Source Code
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
文字列リテラルはに格納されStringConstantPool
ます。
String onlyPool = "Yash";
StringBuilderとStringBufferは可変の文字シーケンスです。つまり、これらのオブジェクトの値を変更できます。StringBufferにはStringBuilderと同じメソッドがありますが、StringBufferの各メソッドは同期されているため、スレッドセーフです。
StringBufferおよびStringBuilderデータは、new演算子を使用してのみ作成できます。したがって、それらはヒープメモリに保存されます。
StringBuilderのインスタンスは、複数のスレッドによる使用には安全ではありません。このような同期が必要な場合は、StringBufferを使用することをお勧めします。
StringBuffer threadSafe = new StringBuffer("Yash");
threadSafe.append(".M");
threadSafe.toString();
StringBuilder nonSync = new StringBuilder("Yash");
nonSync.append(".M");
nonSync.toString();
StringBufferとStringBuilderには、。、replace(int start, int end, String str)
およびのような特別なメソッドが
ありreverse()
ます。
注:StringBufferとSringBuilderは、の実装を提供するため、変更可能です
Appendable Interface
。
どれを使用するか。
値を毎回変更しない場合は、Useを使用することをお勧めしますString Class
。ジェネリックの一部としてComparable<T>
、値をソートまたは比較する場合は、に進みString Class
ます。
//ClassCastException: java.lang.StringBuffer cannot be cast to java.lang.Comparable
Set<StringBuffer> set = new TreeSet<StringBuffer>();
set.add( threadSafe );
System.out.println("Set : "+ set);
StringBufferよりも高速なStringBuilderを使用するたびに値を変更する場合。複数のスレッドが値を変更している場合は、StringBufferに移動します。
Java 5以降を使用している場合は、のStringBuilder
代わりにを使用する必要があることに注意してくださいStringBuffer
。APIドキュメントから:
JDK 5のリリース以降、このクラスには、シングルスレッドで使用するために設計された同等のクラスが追加されました
StringBuilder
。StringBuilder
クラスは同じ操作のすべてをサポートしますが、同期を実行しないため高速ですので、通常、このクラスよりも優先してクラスを使用する必要があります。
実際には、これを複数のスレッドから同時に使用することはほとんどありません。そのため、実行する同期StringBuffer
はほとんど常に不要なオーバーヘッドです。
個人的には、の実際の使用はないと思いますStringBuffer
。文字シーケンスを操作して複数のスレッド間で通信したいときはいつですか?それはまったく役に立たないように思えますが、おそらく私はまだ光を見ていません:)
Stringと他の2つのクラスの違いは、Stringは不変で、他の2つは変更可能なクラスです。
しかし、なぜ同じ目的で2つのクラスがあるのでしょうか。
理由は、それStringBuffer
がスレッドセーフであり、そうStringBuilder
でないことです。
StringBuilder
は新しいクラスでStringBuffer Api
あり、で導入されたものでJDK5
あり、シングルスレッド環境で作業している場合は常に推奨されます。Faster
Javaでは、Stringは不変です。不変であることは、文字列が作成されると、その値を変更できないことを意味します。 StringBufferは変更可能です。StringBufferオブジェクトが作成されたら、新しいオブジェクトを作成する代わりに、オブジェクトの値にコンテンツを追加するだけです。 StringBuilderはStringBufferに似ていますが、スレッドセーフではありません。StingBuilderのメソッドは同期されませんが、他の文字列と比較して、Stringbuilderは最も速く実行されます。これらを実装することで、String、StringBuilder、StringBufferの違いを知ることができます。