java - كم عدد كائنات السلسلة التي سيتم إنشاؤها عند تسلسل سلاسل متعددة؟




string string-concatenation (5)

سُئلت في مقابلة عن عدد الكائنات التي سيتم إنشاؤها على المشكلة المحددة:

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

أجبت أنه سيكون هناك 6 كائنات تم إنشاؤها في تجمع السلسلة.

3 سيكون لكل من المتغيرات الثلاثة.
1 سيكون لـ str1 + str2 (دعنا نقول str ).
1 سيكون ل str2 + str3 .
1 ستكون لـ str + str3 ( str = str1 + str2 ).

هل الجواب الذي أعطيته صحيح؟ إذا لم يكن كذلك ، ما هو الجواب الصحيح؟


تعتمد أي إجابة على سؤالك على تطبيق JVM وإصدار 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 * ، 1 StringBuilder ، 1 تم إنتاج مثيل String ديناميكيًا بواسطة StringBuilder#toString ).

جافا 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

الذي يغير "الإجابة الصحيحة" magically إلى 4 كائنات لأنه لا يوجد StringBuilder وسيطة المعنية.

* دعونا حفر أعمق قليلا.

12.5. إنشاء مثيلات فئة جديدة

قد يتم إنشاء مثيل فئة جديدة ضمنيًا في المواقف التالية:

  • قد يؤدي تحميل فئة أو واجهة تحتوي على سلسلة حرفية ( §3.10.5 ) إلى إنشاء كائن سلسلة جديد لتمثيل الحرفي. (لن يحدث هذا إذا كانت سلسلة تدل على نفس تسلسل نقاط كود 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 هذه لها تأثير جانبي: تمت إضافة المواد الأساسية التي كنت أبحث عنها إلى المجموعة أيضًا. زيادة حجم التجمع بمقدار واحد (تم إضافة "bytes String" ) بعد أن قمت بتطبيق this.contains("bytes String") .

إلو أين سيقودنا هذا الأمر!؟

ليس لدينا أي فكرة عما إذا كان "First" قد تم إنشاؤه وتدربه قبل أن ندعو String str1 = "First"; ، لذلك لا يمكننا التصريح بحزم بأن الخط ينشئ نسخة جديدة.


سيتم إنشاء كائن سلسلة 4 في تجمع ثابت سلسلة. 3 للأحرف و 1 مع السلاسل.

إذا استخدمنا

String s1 = new String("one")

فإنه سيتم إنشاء كائنين واحد في تجمع ثابت والآخر في ذاكرة كومة الذاكرة المؤقتة.

إذا حددنا:

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

فإنه سيتم إنشاء كائنين واحد في تجمع ثابت والآخر في ذاكرة كومة الذاكرة المؤقتة.


مع المعلومات المقدمة ، لا يمكن الإجابة بالتأكيد على السؤال. كما هو مذكور في JLS ، § 15.18.1 :

... لزيادة أداء سلسلة متكررة السلسلة ، قد يستخدم برنامج التحويل البرمجي Java فئة StringBuffer أو تقنية مشابهة لتقليل عدد كائنات السلسلة الوسيطة التي يتم إنشاؤها بواسطة تقييم تعبير.

هذا يعني أن الإجابة تعتمد على الأقل على برنامج التحويل البرمجي Java المستخدم.

أعتقد أن أفضل ما يمكننا فعله هو إعطاء فاصل زمني كإجابة:

  • قد يتمكن المحول البرمجي الذكي من استنتاج عدم استخدام str3 إلى str3 مطلقًا أو طي String أثناء التحويل البرمجي ، بحيث يتم إنشاء واحد فقط من String (المشار إليه بواسطة str4 )
  • يجب أن يكون العدد الأقصى المعقول str3 s الذي تم إنشاؤه هو 5: واحد لكل من str3 إلى str3 وواحد tmp = str1 + str2 وواحد لل str4 = tmp + str3 .

لذلك ... ستكون إجابتي "شيء بين واحد إلى خمسة مشاريع String ". بالنسبة إلى إجمالي عدد الكائنات التي تم إنشاؤها فقط لهذه العملية ... لا أعرف. قد يعتمد هذا أيضًا على كيفية تنفيذ StringBuffer بالضبط.

جانبا: أتساءل ما السبب وراء طرح مثل هذه الأسئلة. عادة ، لا يحتاج المرء إلى الاهتمام بتلك التفاصيل.


من المحتمل أن ينشئ Java 8 5 كائنات:

  • 3 ل 3 حرفية
  • 1 StringBuilder
  • 1 لسلسلة متسلسلة

مع تغير Java 9 ، لم يعد تسلسل StringBuilder يستخدم String .


يمكن لتطبيق Java المتوافق أن يسلسل السلاسل أي عدد من الطرق ، في وقت التشغيل أو في وقت الترجمة ، يحتاج إلى أي عدد من كائنات وقت التشغيل ، بما في ذلك كائنات صفرية إذا اكتشف أن النتيجة ليست مطلوبة في وقت التشغيل.







string-building