ホームページ  >  記事  >  Java  >  Java - StringBuffer と StringBuilder の原則と違い

Java - StringBuffer と StringBuilder の原則と違い

高洛峰
高洛峰オリジナル
2017-01-22 09:44:311973ブラウズ

私は最近仕事を探していたのですが、試験官が私に簡単な質問をしました。「StringBuffer と StringBuilder の違いは何ですか? それらのアプリケーション シナリオは何ですか?」将来的にも、記録としても。

実際、Google で検索すると答えがわかります。StringBuffer と StringBuilder のメソッドと関数は完全に同等ですが、StringBuffer のほとんどのメソッドは synchronized キーワードで変更されているため、スレッド セーフですが、StringBuilder はこの変更はスレッドセーフではないとみなされる可能性があります。

上記の答えをよりよく理解するには、StringBuffer と StringBuilder のソース コードの実装を直接見るのがより現実的です。プログラマとしては、「疑問がある場合はソース コードを確認する」のが正しい方法です。責任を持って言いますが、もちろん条件は整っています。

jdkの実装では、StringBufferもStringBuilderもAbstractStringBuilderを継承していますが、マルチスレッドの安全性と非安全性については、StringBufferのメソッドの前にある同期の塊で大体理解できます。

ここで、AbstractStringBuilder の実装原理についてのカジュアルな話があります: StringBuffer の使用は、Java での文字列接続の効率を向上させるためだけにすぎないことはわかっています。文字列接続に + を直接使用すると、jvm は複数の String オブジェクトを作成するためです。したがって、特定の問題のオーバーヘッドが発生します。 AbstractStringBuilder は、char 配列を使用して、追加する必要がある文字列を保存します。char 配列には初​​期サイズがあり、追加された文字列の長さが現在の char 配列の容量を超えると、char 配列が動的に拡張されます。つまり、より大きな期間が再適用されます。 . メモリ領域を取得し、現在の char 配列を新しい場所にコピーします。メモリの再割り当てとコピーのオーバーヘッドは比較的高いため、メモリ領域を再申請するたびに、メモリ領域よりも大きいメモリ領域を申請する必要があります。現在必要なメモリ容量は 2 倍です。

次は、楽しみましょう!

Googleでいくつかの情報が出てきました:


StringBufferはJDK 1.0で始まりました
StringBuilderはJDK 1.5で始まりました

JDK 1.5からは、文字列変数(+)を使った接続操作がJVM内部で採用され実装されていますこの操作は以前は StringBuffer によって実装されていましたが、
StringBuilder によって実装されました。
]

簡単なプログラムを通してその実行プロセスを見てみましょう:

リスト 1 Buffer.java

public class Buffer {
public static void main(String[] args) {
String s1 = "aaaaa";
String s2 = "bbbbb";
String r = null;
int i = 3694;
r = s1 + i + s2;
 
for(int j=0;i<10;j++){
r+="23124";
}
}
}

コマンド javap -c Buffer を使用して、そのバイトコード実装を表示します:

Java - StringBuffer と StringBuilder の原則と違い

リスト 2 Buffer class bytecode

リスト 1 とリスト 2 を照合します。リスト 2 のバイトコードの ldc 命令は、定数プールからスタックの先頭に文字列「aaaaa」をロードし、istore_1 は変数 1 に「aaaaa」を格納します。後者と同様に、sipush は短い整数定数値 (-32768 ~ 32767) をスタックの先頭にプッシュします。ここでは定数「3694」です。その他の Java 命令セットについては、別の記事「Java 命令セット」を参照してください。

13、13~17 が StringBuffer オブジェクトを新規作成し、その初期化メソッドを呼び出していることを直接見てみましょう。20~21 は、最初に aload_1 を通じて変数 1 をスタックの先頭にプッシュします。前述したように、変数 1 は文字列を保持します。定数「 aaaaa" を結合し、invokevirtual 命令で StringBuffer の append メソッドを呼び出し、"aaaaa" を結合します。以降の 24 ~ 30 秒も同様です。最後に、33 で StringBuffer の toString 関数を呼び出して String の結果を取得し、astore を通じて変数 3 に格納します。

これを見て、「JVM は内部で StringBuffer を使って文字列を接続しているのだから、自分で StringBuffer を使う必要はなく、"+" を使えばいいだけだ!」と言う人もいるかもしれません。それは...ですか?もちろん違います。 「存在には理由がある」という言葉の通り、次のループに対応するバイトコードを見てみましょう。

37~42 は for ループに入る前の準備です。37 と 38 は j を 1 に設定します。ここで 44 は if_icmpge を通じて j と 10 を比較します。j が 10 より大きい場合は、73、つまり return ステートメントに直接ジャンプします。それ以外の場合は、バイトコード 47 ~ 66 のループに入ります。ここでは、コード内の文字列接続を処理するために StringBuffer を使用する必要がある理由を知るために、47 から 51 を見るだけで済みます。「+」操作が実行されるたびに、jvm は、文字列接続を処理するために新しい StringBuffer オブジェクトを作成する必要があるためです。文字列接続は、多くの文字列連結操作が関係する場合にコストがかかる可能性があります。

Java-StringBuffer と StringBuilder の原則と相違点に関するその他の関連記事については、PHP 中国語 Web サイトに注目してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。