web-dev-qa-db-ja.com

String、StringBuffer、およびStringBuilder

StringStringBuffer、およびStringBuilderを比較するリアルタイムの状況を教えてください。

204
JavaUser

可変性の違い:

Stringimmutableです。値を変更しようとすると、別のオブジェクトが作成されますが、StringBufferStringBuildermutableであるため、値を変更できます。

スレッドセーフの違い:

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

状況:

  • Stringオブジェクトは不変であるため、文字列が変更されない場合はStringクラスを使用します。
  • 文字列を変更できる場合(例:文字列の構築における多くのロジックと操作)、単一のスレッドからのみアクセスされる場合は、StringBuilderを使用するだけで十分です。
  • 文字列を変更でき、複数のスレッドからアクセスされる場合は、StringBufferが同期であるため、スレッド変数を使用できるため、StringBufferを使用します。
357
bakkal
  • 不変の構造が適切な場合は、 String を使用します。 Stringから新しい文字シーケンスを取得すると、CPU時間またはメモリのいずれかで許容できないパフォーマンスの低下が生じる場合があります(データがコピーされないため、サブストリングを取得することはCPU効率的ですが、これは潜在的にはるかに大量のデータが割り当てられたままになる可能性があることを意味します)。
  • StringBuilder は、通常、複数の文字シーケンスを連結するために、可変文字シーケンスを作成する必要がある場合に使用します。
  • 同じ状況で StringBuffer を使用しますが、StringBuilderを使用しますが、基になる文字列への変更を同期する必要がある場合(複数のスレッドが文字列バッファーの読み取り/変更を行うため)。

例を参照してください here

46
Artefacto

基本:

Stringは不変のクラスであり、変更できません。 StringBuilder は、追加、変更、削除、および最終的にStringに変換できる可変クラスです。StringBufferは、StringBuilderの元の同期バージョンです。

オブジェクトにアクセスするスレッドが1つしかないすべての場合に、StringBuilderを選択する必要があります。

詳細:

また、StringBuilder/Buffersは魔法ではなく、単にバッキングオブジェクトとして配列を使用するだけであり、配列がいっぱいになったときに配列を再割り当てする必要があることに注意してください。 .append()が呼び出されるたびにサイズを常に変更する必要のない、元々十分な大きさのStringBuilder/Bufferオブジェクトを作成してください。

サイズ変更は非常に退化する可能性があります。基本的に、拡張する必要があるたびに、バッキング配列のサイズを現在のサイズの2倍に変更します。これにより、StringBuilder/Bufferクラスが大きくなり始めると、大量のRAMが割り当てられ、使用されなくなる可能性があります。

JavaでString x = "A" + "B";は舞台裏でStringBuilderを使用します。そのため、単純なケースでは、自分で宣言するメリットはありません。ただし、4k未満などの大きなStringオブジェクトを構築する場合、StringBuilder sb = StringBuilder(4096);の宣言は、連結や デフォルトコンストラクター (16文字のみ)を使用するよりもはるかに効率的です。 Stringが10k未満になる場合は、コンストラクターで10kに初期化して安全にします。しかし、10kに初期化されている場合、10kを超える1文字を書き込むと、再割り当てされて20k配列にコピーされます。したがって、高よりも初期化する方が低よりも優れています。

自動サイズ変更の場合、17番目の文字で補助配列が再割り当てされて32文字にコピーされ、33番目の文字でこれが再び発生し、再割り当てされて配列を64文字にコピーします。これがどのようにlotsに縮退し、最初に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);
    }

ベストプラクティスは、Stringの大きさがすぐにわからないが推測できる場合は、StringBuilder/Bufferを必要以上に大きく初期化することです。必要以上にわずかに多くのメモリを割り当てると、多くの再割り当てやコピーよりも優れたものになります。

また、StringBuilder/BufferStringで初期化することに注意してください。これは、ほとんどの場合、回避しようとしている縮退した再割り当てとコピーサイクルを開始するだけのString + 16文字のサイズを割り当てます。以下は、Java 6ソースコードから直接抜粋したものです。

public StringBuilder(String str) {
    super(str.length() + 16);
    append(str);
    }

偶然作成しておらず、呼び出されたコンストラクターを制御できないStringBuilder/Bufferのインスタンスになった場合、縮退した再割り当てとコピーの動作を回避する方法があります。 .ensureCapacity() を呼び出して、結果のStringが収まるようにします。

代替案:

ちょうどメモとして、本当にheavyStringの構築と操作を行う場合、 Ropes と呼ばれるよりパフォーマンス指向の代替があります=。

別の代替方法は、ArrayList<String>をサブクラス化してStringList実装を作成し、カウンターを追加してすべての.append()およびリストの他の突然変異操作の文字数を追跡し、.toString()をオーバーライドして、必要な正確なサイズのStringBuilderを作成することですリストを介して出力を作成すると、そのStringBuilderをインスタンス変数にして、.toString()の結果を「キャッシュ」し、何か変更があった場合にのみ再生成することができます。

また、固定形式の出力を作成する場合はString.format()を忘れないでください。これは、コンパイラが改善するため最適化できます。

27
user177800

連結の意味ですか?

実世界の例:他の多くの文字列から新しい文字列を作成したい

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

ひも

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 vs. StringBuilder

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

したがって、単一のスレッドで複数回呼び出すと(ケースの90%)、StringBuilderは、スレッドロックを所有しているかどうかを確認するために停止しないため、muchより高速に実行されます。

したがって、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の行は、「リテラル」という名前の文字列用です。

31〜53の行は、「builder」という名前の文字列用です。

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

9
OscarRyz

String Family

文字列

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

文字列オブジェクトはimmutableであり、一度作成されると変更できません。 (文字列は定数です

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

    • Java言語は、文字列連結演算子(+)、および他のオブジェクトの文字列への変換に特別なサポートを提供します。文字列の連結は、StringBuilder(またはStringBuffer)クラスとそのappendメソッドを通じて実装されます。
    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()などの特別なメソッドがあります。

    NOTE:StringBufferとSringBuilderはAppendable Interfaceの実装を提供するため、可変です。


どちらを使用するか。

  • 値を毎回変更しない場合は、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);
    
  • StringBuilderを使用するたびに値を変更する場合は、StringBufferよりも高速です。複数のスレッドが値を変更している場合は、StringBufferを使用します。

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

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

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

4
Lars Andren

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

リリースJDK 5の時点で、このクラスは、単一のスレッドStringBuilderで使用するために設計された同等のクラスで補足されています。 StringBuilderクラスは、同じ操作をすべてサポートしますが、同期を実行しないため高速であるため、通常はこのクラスよりも優先して使用する必要があります。

実際には、複数のスレッドから同時にこれを使用することはほとんどないため、StringBufferが行う同期はほとんどの場合不要なオーバーヘッドです。

3
Jesper

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

3
fredoverflow

Stringと他の2つのクラスの違いは、Stringが不変であり、他の2つが可変クラスであることです。

しかし、なぜ同じ目的のために2つのクラスがあるのですか?

理由は、StringBuffername__はスレッドセーフであり、StringBuildername__はスレッドセーフではないからです。 StringBuildername__はStringBuffer Apiの新しいクラスであり、JDK5で導入されました。また、Fastername__であるため、シングルスレッド環境で作業している場合は常に推奨されます。

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

3
Hitesh Garg

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

2
rajat ghai