web-dev-qa-db-ja.com

複数の文字列を連結すると、いくつの文字列オブジェクトが作成されますか?

与えられた問題で作成されるオブジェクトの数についてインタビューで尋ねられました:

String str1 = "First";
String str2 = "Second";
String str3 = "Third";
String str4 = str1 + str2 + str3;

文字列プールに6オブジェクトが作成されると答えました。

3は3つの変数のそれぞれに対応します。
1はstr1 + str2strとしましょう)。
1はstr2 + str3
1はstr + str3str = str1 + str2)。

私が出した答えは正しいですか?そうでない場合、正しい答えは何ですか?

36
bhpsh

あなたの質問に対する答えは、JVMの実装と現在使用されているJava=バージョンによって異なります。インタビューで尋ねるのは不合理な質問だと思います。

Java 8

私のマシンでは、Java 1.8.0_201の場合、スニペットはこのバイトコードになります

_L0
 LINENUMBER 13 L0
 LDC "First"
 ASTORE 1
L1
 LINENUMBER 14 L1
 LDC "Second"
 ASTORE 2
L2
 LINENUMBER 15 L2
 LDC "Third"
 ASTORE 3
L3
 LINENUMBER 16 L3
 NEW Java/lang/StringBuilder
 DUP
 INVOKESPECIAL Java/lang/StringBuilder.<init> ()V
 ALOAD 1
 INVOKEVIRTUAL Java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
 ALOAD 2
 INVOKEVIRTUAL Java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
 ALOAD 3
 INVOKEVIRTUAL Java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
 INVOKEVIRTUAL Java/lang/StringBuilder.toString ()Ljava/lang/String;
 ASTORE 4
_

これは、5個のオブジェクトが作成されていることを証明します(3 String literals *、1 StringBuilder =、 _StringBuilder#toString_ )によって動的に生成されたStringインスタンス。

Java 12

私のマシンでは、Java 12.0.2で、バイトコードは

_// identical to the bytecode above
L3
 LINENUMBER 16 L3
 ALOAD 1
 ALOAD 2
 ALOAD 3
 INVOKEDYNAMIC makeConcatWithConstants(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; [
  // handle kind 0x6 : INVOKESTATIC
  Java/lang/invoke/StringConcatFactory.makeConcatWithConstants(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
  // arguments:
  "\u0001\u0001\u0001"
 ]
 ASTORE 4
_

which magically は、「正しい答え」を4個のオブジェクトに変更します。これは、中間のStringBuilderが含まれていないためです。


*もう少し掘り下げましょう。

12.5。新しいクラスインスタンスの作成

次の状況では、新しいクラスインスタンスが暗黙的に作成される場合があります。

  • 文字列リテラル( §3.10.5 )を含むクラスまたはインターフェースをロードすると、リテラルを表す新しいStringオブジェクトが作成される場合があります。 (これは、同じUnicodeコードポイントのシーケンスを示す文字列が以前にインターンされている場合は発生しません。)

つまり、アプリケーションを起動すると、文字列プールには既にオブジェクトが存在します。あなたはそれらが何であるか、そしてそれらがどこから来るのかほとんど理解していません(あなたがそれらに含まれるすべてのリテラルについてすべてのロードされたクラスをスキャンしない限り)。

_Java.lang.String_クラスは間違いなく必須のJVMクラスとしてロードされます。つまり、そのすべてのリテラルが作成され、プールに配置されます。

Stringのソースコードからランダムに選択されたスニペットを取得し、そこからいくつかのリテラルを選択し、プログラムの最初にブレークポイントを配置し、プールにこれらのリテラルが含まれているかどうかを調べます。

_public final class String
    implements Java.io.Serializable, Comparable<String>, CharSequence,
               Constable, ConstantDesc {
    ...
    public String repeat(int count) {
        // ... 
        if (Integer.MAX_VALUE / count < len) {
            throw new OutOfMemoryError("Repeating " + len + " bytes String " + count +
                    " times will produce a String exceeding maximum size.");
        }
    }
    ...
}
_

彼らは確かにそこにいます。

興味深い発見として、このIDEAのフィルタリングには副作用があります。私が探していた部分文字列もプールに追加されました。 this.contains("bytes String")を適用した後、プールサイズが1増加しました(_"bytes String"_が追加されました)。

これはどこに私たちを残しますか?

_"First"_を呼び出す前に_String str1 = "First";_が作成され、インターンされたかどうかはわかりません。そのため、この行が新しいインスタンスを作成することを明確に述べることはできません。

31
Andrew Tobilko

与えられた情報では、質問に明確に答えることはできません。 JLS、§15.18.1 で述べられているように:

...繰り返される文字列連結のパフォーマンスを向上させるために、Javaコンパイラは、StringBufferクラスまたは同様の手法を使用して、式の評価。

これは、答えが少なくとも具体的なJava使用されるコンパイラに依存することを意味します。

私たちができる最善のことは、答えとして間隔を与えることです:

  • スマートコンパイラは、str1からstr3が使用されないことを推測し、String- objectが1つだけ作成されるように、コンパイル中に連結を折りたたむことができます(str4によって参照されるオブジェクト)
  • 作成されるStringsの実用的な最大数は5です。str1からstr3まで1つずつ、tmp = str1 + str2用に1つ、str4 = tmp + str3用に1つ。

だから...私の答えは「1〜5個のString- objectsの間の何か」でしょう。この操作のために作成されたオブジェクトの総数については...わかりません。これは、たとえば、 StringBufferが実装されています。

余談ですが、そのような質問をする背後にある理由は何でしょうか。通常、これらの詳細を気にする必要はありません。

18
Turing85

Java 8はおそらく5つのオブジェクトを作成します:

  • 3つのリテラルには3
  • 1 StringBuilder
  • 連結されたStringの場合は1

Java 9 things changed を使用すると、String連結はStringBuilderを使用しなくなります。

8
Puce

5である必要があります。

  • 3つのリテラルに3つ(str1str2およびstr3に割り当てられます)

  • str1 + str2用に1つ

  • (result from the previous operation) + str3用に1つ(str4に割り当て済み)

4
Laurenz Albe

適合Java実装は、実行時またはコンパイル時に、文字列をいくつでも連結できます。結果がそうでないことを検出した場合はゼロオブジェクトを含む、任意の数の実行時オブジェクトが必要です。実行時に必要です。

3
Boann

4つの文字列オブジェクトが文字列定数プールに作成されます。リテラルの場合は3、連結の場合は1。

私たちが使うなら

String s1 = new String("one")

2つのオブジェクトが1つは定数プールに、もう1つはヒープメモリに作成されます。

定義すると:

String s1 = "one";
String s2 = new String("one");

2つのオブジェクトが1つは定数プールに、もう1つはヒープメモリに作成されます。

2
Parmar Kamlesh

連結操作では、これらの多くのStringオブジェクトは作成されません。 a StringBuilderを作成し、文字列を追加します。したがって、5つのオブジェクト、3(変数)+ 1(sb)+ 1(連結文字列)が存在する可能性があります。

1
javaaddict