551

文字列 a と b を仮定します。

a += b
a = a.concat(b)

ボンネットの下では、それらは同じものですか?

ここでは、参照として逆コンパイルされた concat を示します。+オペレーターも逆コンパイルして、それが何をするかを確認できるようにしたいと思います。

public String concat(String s) {

    int i = s.length();
    if (i == 0) {
        return this;
    }
    else {
        char ac[] = new char[count + i];
        getChars(0, count, ac, 0);
        s.getChars(0, i, ac, count);
        return new String(0, count + i, ac);
    }
}
4

12 に答える 12

616

いいえ、そうではありません。

まず、セマンティクスにわずかな違いがあります。の場合、 をスローしますがa、の元の値を であるかのように扱います。さらに、メソッドは値のみを受け入れますが、オペレーターは暗黙のうちに引数を文字列に変換します (オブジェクトのメソッドを使用)。そのため、メソッドは受け入れるものにおいてより厳密です。nulla.concat(b)NullPointerExceptiona+=banullconcat()String+toString()concat()

内部を調べるには、単純なクラスを次のように記述します。a += b;

public class Concat {
    String cat(String a, String b) {
        a += b;
        return a;
    }
}

javap -c(Sun JDKに含まれています)で逆アセンブルします。次のリストが表示されます。

java.lang.String cat(java.lang.String, java.lang.String);
  Code:
   0:   new     #2; //class java/lang/StringBuilder
   3:   dup
   4:   invokespecial   #3; //Method java/lang/StringBuilder."<init>":()V
   7:   aload_1
   8:   invokevirtual   #4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   11:  aload_2
   12:  invokevirtual   #4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   15:  invokevirtual   #5; //Method java/lang/StringBuilder.toString:()Ljava/lang/    String;
   18:  astore_1
   19:  aload_1
   20:  areturn

だから、a += b同等です

a = new StringBuilder()
    .append(a)
    .append(b)
    .toString();

concatメソッドはより高速である必要があります。ただし、StringBuilder少なくともパフォーマンスの点では、より多くの文字列を使用すると、メソッドが優先されます。

Stringand (およびそのパッケージ プライベート基本クラス)のソース コードはStringBuilder、Sun JDK の src.zip で入手できます。char 配列を構築し (必要に応じてサイズを変更)、最終的なString. 実際のメモリ割り当ては驚くほど高速です。

更新: Pawel Adamski が指摘しているように、最近の HotSpot ではパフォーマンスが変化しています。javacまったく同じコードを生成しますが、バイトコード コンパイラはごまかします。コードの本体全体が破棄されるため、単純なテストは完全に失敗します。Summing System.identityHashCode(not String.hashCode) は、StringBufferコードにわずかな利点があることを示しています。次の更新がリリースされるとき、または別の JVM を使用する場合は、変更される可能性があります。@lukasederから、HotSpot JVM 組み込み関数のリスト

于 2008-09-06T16:25:37.163 に答える
97

Niyazは正しいですが、特殊な + 演算子を Java コンパイラによってより効率的なものに変換できることも注目に値します。Java には、非スレッドセーフで変更可能な String を表す StringBuilder クラスがあります。一連の文字列連結を実行するとき、Java コンパイラはサイレントに変換します

String a = b + c + d;

の中へ

String a = new StringBuilder(b).append(c).append(d).toString();

大きな文字列の場合、これははるかに効率的です。私の知る限り、これは concat メソッドを使用する場合には発生しません。

ただし、空の文字列を既存の文字列に連結する場合は、concat メソッドの方が効率的です。この場合、JVM は新しい String オブジェクトを作成する必要はなく、単純に既存のオブジェクトを返すことができます。これを確認するには、concat のドキュメントを参照してください。

したがって、効率を非常に重視する場合は、空の可能性のある文字列を連結するときに concat メソッドを使用し、それ以外の場合は + を使用する必要があります。ただし、パフォーマンスの違いはごくわずかであり、おそらくこれについて心配する必要はありません。

于 2008-09-06T16:24:09.227 に答える
49

@marcioと同様のテストを実行しましたが、代わりに次のループを使用しました。

String c = a;
for (long i = 0; i < 100000L; i++) {
    c = c.concat(b); // make sure javac cannot skip the loop
    // using c += b for the alternative
}

念のため、私も投げ入れましStringBuilder.append()た。各テストは10回実行され、実行ごとに10万回の繰り返しが行われました。結果は次のとおりです。

  • StringBuilder勝ちます。ほとんどの実行でクロック時間の結果は0であり、最長の実行には16ミリ秒かかりました。
  • a += b実行ごとに約40000ms(40秒)かかります。
  • concat1回の実行で必要なのは10000ms(10s)のみです。

クラスを逆コンパイルして内部を確認したり、プロファイラーで実行したりはまだしていませんが、の新しいオブジェクトを作成してからに変換することにa += b多くの時間を費やしていると思われます。StringBuilderString

于 2008-09-06T19:25:12.847 に答える
23

Tom は、+ 演算子が何をするかを正確に説明しています。一時的な を作成しStringBuilder、パーツを追加して、 で終了しtoString()ます。

ただし、これまでの回答はすべて、HotSpot ランタイム最適化の効果を無視しています。具体的には、これらの一時的な操作は一般的なパターンとして認識され、実行時により効率的なマシン コードに置き換えられます。

@marcio:マイクロベンチマークを作成しました。最新の JVM では、これはコードをプロファイリングする有効な方法ではありません。

実行時の最適化が重要な理由は、コードのこれらの違いの多く (オブジェクトの作成を含む) が、HotSpot が開始されると完全に異なるからです。確実に知る唯一の方法は、コードを in situでプロファイリングすることです。

最後に、これらの方法はすべて、実際には信じられないほど高速です。これは、時期尚早の最適化のケースである可能性があります。文字列を頻繁に連結するコードがある場合、最大速度を得る方法は、選択した演算子とは関係なく、代わりに使用しているアルゴリズムとはおそらく関係ありません!

于 2008-09-06T18:38:28.887 に答える
21

簡単なテストはいかがですか?以下のコードを使用しました:

long start = System.currentTimeMillis();

String a = "a";

String b = "b";

for (int i = 0; i < 10000000; i++) { //ten million times
     String c = a.concat(b);
}

long end = System.currentTimeMillis();

System.out.println(end - start);
  • "a + b"バージョンは 2500ms で実行されまし
  • 1200msa.concat(b)で実行されます

数回テストしました。バージョンのconcat()実行にかかった時間は、平均で半分でした。

concat()メソッドは常に新しい文字列を作成するため、この結果には驚きました(" new String(result)" が返されます)。次のことはよく知られています。

String a = new String("a") // more than 20 times slower than String a = "a"

常に同じ文字列になることがわかっているのに、コンパイラが「a + b」コードでの文字列作成を最適化できなかったのはなぜですか? 新しい文字列の作成を回避できます。上記の説明が信じられない場合は、自分でテストしてください。

于 2008-09-06T17:59:20.713 に答える
6

concat基本的に、+ とメソッドには 2 つの重要な違いがあります。

  1. concatメソッドを使用している場合は、文字列を連結することしかできませんが、 +演算子の場合は、文字列を任意のデータ型と連結することもできます。

    例えば:

    String s = 10 + "Hello";
    

    この場合、出力は10Helloになります。

    String s = "I";
    String s1 = s.concat("am").concat("good").concat("boy");
    System.out.println(s1);
    

    上記の場合、必須の 2 つの文字列を指定する必要があります。

  2. +concatの 2 番目の主な違いは次のとおりです。

    ケース 1:この方法でconcat演算子を 使用して同じ文字列を連結するとします。

    String s="I";
    String s1=s.concat("am").concat("good").concat("boy");
    System.out.println(s1);
    

    この場合、プールで作成されるオブジェクトの総数は次のように 7 です。

    I
    am
    good
    boy
    Iam
    Iamgood
    Iamgoodboy
    

    ケース 2:

    +演算子を使用して同じ文字列を連結します。

    String s="I"+"am"+"good"+"boy";
    System.out.println(s);
    

    上記の場合、作成されるオブジェクトの総数はわずか 5 です。

    実際に+演算子を使用して文字列を連結すると、次のように同じタスクを実行する StringBuffer クラスが維持されます。

    StringBuffer sb = new StringBuffer("I");
    sb.append("am");
    sb.append("good");
    sb.append("boy");
    System.out.println(sb);
    

    このようにして、5 つのオブジェクトのみが作成されます。

つまり、これらは+concatメソッドの基本的な違いです。楽しみ :)

于 2014-08-19T07:49:00.190 に答える
3

私はそうは思わない。

a.concat(b)は String で実装されており、実装は初期の Java マシンからあまり変わっていないと思います。操作の+実装は、Java のバージョンとコンパイラに依存します。現在、操作を可能な限り高速にするために+を使用して実装されています。StringBufferたぶん将来、これは変わるでしょう。以前のバージョンの Javaでは、文字列+に対する操作は中間結果を生成するため、非常に低速でした。

+=それはを使用して実装され+、同様に最適化されていると思います。

于 2008-09-06T16:22:39.230 に答える
2

+ 演算子は、文字列と文字列、char、integer、double、または float データ型の値の間で使用できます。連結する前に値を文字列表現に変換するだけです。

concat 演算子は、文字列に対してのみ実行できます。データ型の互換性をチェックし、一致しない場合はエラーをスローします。

これを除いて、提供したコードは同じことを行います。

于 2008-09-06T16:16:06.547 に答える