String、StringBuffer、およびStringBuilder


213

比較するために、私のリアルタイムの状況を教えてくださいStringStringBufferStringBuilder

回答:


378

変異性の違い:

String不変です。値を変更しようとすると、別のオブジェクトが作成されますが、StringBufferおよびStringBuilder変更可能であるため、値を変更できます。

スレッドセーフの違い:

違いStringBufferとはStringBuilderつまりStringBuffer、スレッドセーフです。したがって、アプリケーションを単一スレッドでのみ実行する必要がある場合は、を使用することをお勧めしますStringBuilderStringBuilderよりも効率的ですStringBuffer

状況:

  • 文字列が変更されない場合は、Stringオブジェクトが不変であるため、Stringクラスを使用します。
  • 文字列が変更され(例:文字列の構築における多くのロジックと操作)、単一のスレッドからのみアクセスされる場合は、aを使用するStringBuilderだけで十分です。
  • 文字列が変更される可能性があり、複数のスレッドからアクセスされる場合は、スレッドの安全性を確保するStringBufferためにStringBufferが同期であるので、を使用します。

16
また、JVMは文字列をバイトコード内のStringBufferに変換するため、論理演算にStringを使用するのはかなり遅く、まったくお勧めできません。StringからStringBufferに変換してから再びStringに戻すと、多くのオーバーヘッドが無駄になります。
Pieter van Niekerk

2
したがってStrings、値を変更すると、別のオブジェクトが作成されます。古いオブジェクト参照は無効化されているので、によってガベージコレクションされる可能性がありますGCか、それともガベージコレクションされますか?
Harsh Vardhan 2014年

@PietervanNiekerk論理演算とはどういう意味ですか?
emlai

論理演算とは基本的なStringの操作ですが、@ Peterが述べたように、すべての場合でStringの代わりにStringBufferの使用を開始する必要がありますか、それとも特定の場合がありますか?
JavaDragon 2016年

@bakkalすることができます私はすべてのメソッドを使用StringしてStringBuilder
roottraveller 2016年

47
  • String不変構造が適切な場合に使用します。から新しい文字シーケンスを取得するとString、CPU時間またはメモリのいずれかで、許容できないパフォーマンスペナルティが発生する可能性があります(データがコピーされないため、部分文字列の取得はCPU効率が良いですが、これは、はるかに大量のデータが割り当てられたままになる可能性があることを意味します)。
  • StringBuilder変更可能な文字シーケンスを作成する必要がある場合に使用します。通常、複数の文字シーケンスを連結します。
  • を使用StringBufferするのと同じ状況で使用しますがStringBuilder、基になる文字列への変更を同期する必要がある場合(複数のスレッドが文字列バッファーを読み取り/変更しているため)。

こちらの例をご覧ください


2
簡潔ですが不完全です。これは、StringBuilder / Bufferを使用する根本的な理由を見逃しており、通常の文字列連結動作の再割り当てと配列コピーを削減または排除するためです。

1
「不変の文字列を扱う場合、文字列を使用します」-意味がありません。文字列のインスタンスは不変であるため、コメントは「不変性によるメモリ使用量が問題ではない場合は文字列を使用する」となるはずです。受け入れられた答えは、それが根拠をかなりカバーしています。
fooMonster 2011

28

基礎:

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()、固定形式の出力を作成する場合も忘れないでください。これらは、コンパイラーによって改善されるため、コンパイラーによって最適化できます。


1
String x = "A" + "B";本当にStringBuilderのようにコンパイル?なぜにコンパイルしないのですか?String x = "AB";コンポーネントがコンパイル時に不明な場合にのみ、StringBuilderを使用する必要があります。
Matt Greer

それは文字列定数を最適化するかもしれません、私は前回バイトコードを逆コンパイルしたことを思い出すことはできませんが、そこに変数がある場合、確かにStringBuilder実装を使用することを知っています。JDKソースコードをダウンロードして、自分で調べることができます。「A」+「B」は確かに不自然な例です。

String.format()について疑問に思っていました。これがプロジェクトで使用されるのを見たことがありません。通常はStringBuilderです。まあ、人々は怠惰なので、通常は実際には "A" + "B" + "C"です;)たとえ2つだけの文字列が連結されていたとしても、常にStringBuilderを使用する傾向がありました。 。導入されたJDKを思い出せなかったので、主にString.format()を使用したことはありません。
jamiebarrow

9

連結するということですか?

実際の例: 他の多くの文字列から新しい文字列を作成したいとします

たとえば、メッセージを送信するには:

ストリング

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とまったく同じで、効果は異なります)

StringBufferStringBuilder

前者は同期され、後で同期されません。

したがって、単一のスレッドでそれを数回呼び出すと(ケースの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までの番号が付けられた行は、「ビルダー」という名前の文字列用です。

違いはありません。両方の文字列に対してまったく同じコードが実行されます。


2
これは本当に悪い例です。「Dear」を使用してStringBuilderを初期化すると、最初の.append()によって再割り当てとコピーが行われます。「通常の」連結を超えて取得しようとしている効率を完全に無効にします。より良い例は、最終的な文字列の内容全体を保持する初期サイズで作成することです。

1
StringBuilder割り当ての右側でaを使用して文字列連結を行うことは、一般的には良い習慣ではありません。良い実装は、あなたが言うように裏でStringBuilderを使用します。さらに、の例は"a" + "b"1つのリテラルにコンパイルされ"ab"ますが、それを使用StringBuilderすると、2つの不要な呼び出しが発生しappend()ます。
Mark Peters、

@マーク使用するつもりはありませんでし"a"+"b"たが、文字列の連結とは何かを明示的に変更しました。あなたが言わないことは、なぜそれを行うのは良い習慣ではないということです。これがまさに(最新の)コンパイラが行うことです。@fuzzy、私は同意します。特に、最終的な文字列のサイズが(aprox)になるとわかっている場合はそうです。
OscarRyz

1
私はそれが特に悪い習慣だとは思いませんが、私は確かにそれを一般的に行うことを勧めたくありません。それは不格好で読みにくく、過度に冗長です。加えて、それはあなたがそうでなければ1つとしてコンパイルされるかもしれない2つのリテラルを分解しているあなたのような間違いを助長します。プロファイリングが私にそれが違いをもたらしたと言った場合のみ、私はあなたのやり方でそれを行います。
マークピーターズ

@Markわかりました。私は「テンプレートのようなコードの大きな塊」についてもっと考えていましたが、実際にはすべての通常の文字列リテラルではありませんでした。しかし、はい、私は同意します。彼らは今日同じことをしているので意味がありません(10年前はコードリビジョンの変更を拒否する理由でした):)
OscarRyz

8
-------------------------------------------------- --------------------------------
                  String StringBuffer StringBuilder
-------------------------------------------------- --------------------------------                 
保管エリア| 定数文字列プールヒープヒープ
変更可能| いいえ(不変)はい(変更可能)はい(変更可能)
スレッドセーフ| はいはいいいえ
 パフォーマンス| 速いとても遅い速い
-------------------------------------------------- --------------------------------

Stringのパフォーマンスが速く、StringBufferのパフォーマンスが非常に遅いのはなぜですか?
gaurav 2018年

1
@gauravあなたはそのソースコードを読むことができ、StringBufferのすべてのメソッドがそうでありsynchronised、それが理由です。
ハーレン

8

ストリングファミリー

ストリング

String class文字列を表します。など、Javaプログラムのすべての文字列リテラルは"abc"、このクラスのインスタンスとして実装されます。

Stringオブジェクトは不変彼らは我々が変更することはできません作成された後。(文字列は定数です

  • 文字列がコンストラクタまたはメソッドを使用して作成された場合、それらの文字列はと同様にヒープメモリに格納されSringConstantPoolます。ただし、プールに保存する前に、intern()equalsメソッドを使用して、プール内の同じコンテンツでオブジェクトの可用性を確認するメソッドを呼び出します。String-copyがプールで利用可能な場合、参照を返します。それ以外の場合、Stringオブジェクトがプールに追加され、参照が返されます。

    • Java言語は、文字列連結演算子(+)、および他のオブジェクトから文字列への変換を特別にサポートしています。文字列の連結は、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";

StringBuilderStringBufferは可変の文字シーケンスです。つまり、これらのオブジェクトの値を変更できます。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に移動します。


4

また、StringBufferスレッドセーフですが、スレッドセーフでStringBuilderはありません。

したがって、さまざまなスレッドがアクセスしているリアルタイムの状況でStringBuilderは、結果が不確定になる可能性があります。


3

Java 5以降を使用している場合は、のStringBuilder代わりにを使用する必要があることに注意してくださいStringBuffer。APIドキュメントから:

JDK 5のリリース以降、このクラスには、シングルスレッドで使用するために設計された同等のクラスが追加されましたStringBuilderStringBuilderクラスは同じ操作のすべてをサポートしますが、同期を実行しないため高速ですので、通常、このクラスよりも優先してクラスを使用する必要があります。

実際には、これを複数のスレッドから同時に使用することはほとんどありません。そのため、実行する同期StringBufferはほとんど常に不要なオーバーヘッドです。


3

個人的には、の実際の使用はないと思いますStringBuffer。文字シーケンスを操作して複数のスレッド間で通信したいときはいつですか?それはまったく役に立たないように思えますが、おそらく私はまだ光を見ていません:)


3

Stringと他の2つのクラスの違いは、Stringは不変で、他の2つは変更可能なクラスです。

しかし、なぜ同じ目的で2つのクラスがあるのでしょうか。

理由は、それStringBufferがスレッドセーフであり、そうStringBuilderでないことです。 StringBuilderは新しいクラスでStringBuffer Apiあり、で導入されたものでJDK5あり、シングルスレッド環境で作業している場合は常に推奨されます。Faster

詳細については、http://www.codingeek.com/java/stringbuilder-and-stringbuffer-a-way-to-create-mutable-strings-in-java/をご覧ください。


2

Javaでは、Stringは不変です。不変であることは、文字列が作成されると、その値を変更できないことを意味します。 StringBufferは変更可能です。StringBufferオブジェクトが作成されたら、新しいオブジェクトを作成する代わりに、オブジェクトの値にコンテンツを追加するだけです。 StringBuilderStringBufferに似ていますが、スレッドセーフではありません。StingBuilderのメソッドは同期されませんが、他の文字列と比較して、Stringbuilderは最も速く実行されます。これらを実装することで、String、StringBuilder、StringBufferの違いを知ることができます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.