web-dev-qa-db-ja.com

ベストプラクティス/パフォーマンス:StringBuilder.appendとString.concatの混合

私は、ベストプラクティスとは何か、さまざまなケースで文字列リテラルと変数を連結する理由を理解しようとしています。たとえば、このようなコードがある場合

StringBuilder sb = new StringBuilder("AAAAAAAAAAAAA")
    .append(B_String).append("CCCCCCCCCCC").append(D_String)
    .append("EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE")
    .append("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");

これはそれを行う方法ですか? この投稿 から、Stringsの+演算子がStringBuilderの新しいインスタンスを作成し、オペランドを連結し、String変換を返すことに気付きました。 .append();それが本当なら、それは問題外です。しかし、String.concat()はどうですか?すべての連結に.append()を使用するのは適切ですか?または、変数だけで、リテラルは.concat()で追加しても大丈夫ですか?

StringBuilder sb = new StringBuilder("AAAAAAAAAAAAA")
    .append(B_String.concat("CCCCCCCCCCC")).append(D_String
    .concat("EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE")
    .concat("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"));

これらの状況に対処するためのベストプラクティスとパフォーマンスの一般的なルールは何ですか? +についての私の仮定は正しいのですか、実際には使用すべきではありませんか?

82
Nick Rolando

+演算子

String s = s1 + s2

舞台裏では、これは次のように翻訳されます。

String s = new StringBuilder(s1).append(s2).toString();

ここにs1 + s2がある場合、どれだけ余分な作業が追加されるか想像してください:

stringBuilder.append(s1 + s2)

の代わりに:

stringBuilder.append(s1).append(s2)

+を含む複数の文字列

次のことに注意してください:

String s = s1 + s2 + s3 + ... +sN

に翻訳されます:

String s = new StringBuilder(s1).append(s2).append(s3)...apend(sN).toString();

concat()

String s = s1.concat(s2);

Stringは、char[]s1の両方に適合するs2配列を作成します。 s1およびs2の内容をこの新しい配列にコピーします。実際には、+演算子よりも少ない作業で済みます。

StringBuilder.append()

必要に応じて大きくなる内部char[]配列を維持します。内部のものが十分に大きい場合、余分なchar[]は作成されません。

stringBuilder.append(s1.concat(s2))

また、s1.concat(s2)が余分なchar[]配列を作成し、s1およびs2をコピーして、その新しい配列の内容を内部StringBuilderchar[]にコピーするため、パフォーマンスが低下します。

そうは言っても、常にappend()を使用し、生の文字列を追加する必要があります(最初のコードスニペットは正しいです)。

175

コンパイラは+連結を最適化します。

そう

int a = 1;
String s = "Hello " + a;

に変換されます

new StringBuilder().append("Hello ").append(1).toString();

優れたトピック here で+演算子を使用する理由を説明しています。

15
user973999

最適化はコンパイラーによって自動的に行われます。

Java2コンパイラは、次を自動的に変換します。

String s = s1 + s2; 

String s = (new StringBuffer()).append(s1).append(s2).toString();

オラクルのWebサイトの Java Best Practices から直接引用。

3
BoldAsLove

常にappendを使用する必要があります。

concatは、+のように新しい文字列を作成します。

concatを使用するか、最後の2つの文字列で+を使用すると、JVMは最適化を行うことができるため、この場合に追加するのと同じになります。

2
alain.janinm

正確に2つの文字列を連結する場合は、String.concatを使用します(両方の文字列に適合する新しい文字配列を作成して新しい文字列を作成し、両方の文字列の文字配列をコピーします)。

1行で複数の(3つ以上の)文字列を連結する場合は、+またはStringBuilder.appendを使用します。コンパイラは+をStringBuilder.appendに変換するため、問題はありません。これは、必要に応じて大きくなる1つのchar配列を維持するため、複数の文字列に適しています。

複数の行で複数の文字列を連結する場合、1つのStringBuilderを作成し、append-methodを使用します。最後に、StringBuilderに文字列を追加し終わったら、その.toString()メソッドを使用して、そこから文字列を作成します。複数の行で連結する場合、2番目の方法は各行に新しいStringBuilderを作成し、文字列を追加してからStringにキャストするため、2番目の方法よりも高速です。3番目の方法では、全体に対して1つのStringBuilderのみを使用します。

1
Dakkaron

+演算子を使用するのがベストプラクティスであり、シンプルで読みやすいものです。

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

公式ドキュメント: https://docs.Oracle.com/javase/8/docs/api/Java/lang/String.html

0
D Vy

バイトコードレベルでは違いはなく、効率性を損ないません。バイトコードレベルを実行する場合、appendを呼び出して、+の非インライン演算子オーバーロードメソッドを実行する必要があります。次に、アセンブリ言語レベルで(JavaはCで記述され、CはAssemblyに類似したアセンブリを生成し、スタックへのストア+メソッド呼び出しに対する追加のレジスタ呼び出しがあり、追加のプッシュがあります(実際、クロスコンパイラは+演算子を最適化する可能性がありますその場合、効率との違いはありません。)

読みやすさを向上させる1つの方法があることは良い習慣です。 :)

0
Shevon Silva