الأصل: إنشاء سلسلة Java باستخدام "" أو المُنشئ؟ بواسطة X Wang في Java، يمكن إنشاء سلسلة باستخدام طريقتين:
String x = "abc";
String y = new String("abc");
ما الفرق بين استخدام علامات الاقتباس المزدوجة واستخدام المُنشئ؟
1. الاقتباسات المزدوجة مقابل الاقتباسات المزدوجة البناء
يمكن الإجابة على هذا السؤال من خلال النظر إلى مثالين بسيطين. مثال 1:String a = "abcd";
String b = "abcd";
System.out.println(a == b); // True
System.out.println(a.equals(b)); // True
a==b
صحيح لأنهما يشيران إلى نفس الكائن - سلسلة تم تعريفها كسلسلة حرفية (سلسلة حرفية أدناه) في منطقة الطريقة (نحيل القارئ إلى المصدر الموجود على موردنا: أفضل 8 a
مخططات لفهم Java ، الرسم البياني 8). عندما يتم إنشاء نفس السلسلة الحرفية أكثر من مرة، يتم تخزين نسخة واحدة فقط من السلسلة في الذاكرة، مثيل واحد فقط منها (في حالتنا "abcd"). وهذا ما يسمى "تدريب السلسلة". يتم تلقائيًا إدخال جميع ثوابت السلسلة التي تتم معالجتها في وقت الترجمة في Java. مثال 2: b
String c = new String("abcd");
String d = new String("abcd");
System.out.println(c == d); // False
System.out.println(c.equals(d)); // True
c==d
خطأ لأنها c
تشير d
إلى كائنين مختلفين في الذاكرة (في الكومة). الكائنات المختلفة لها دائمًا مراجع مختلفة. يوضح هذا الرسم البياني الحالتين الموضحتين أعلاه:
2. سلاسل التدريب في مرحلة تنفيذ البرنامج
يشكر المؤلف LukasEder (التعليق أدناه خاص به): يمكن أن يحدث تدريب السلسلة أيضًا أثناء تنفيذ البرنامج، حتى لو تم إنشاء سلسلتين باستخدام المنشئات:String c = new String("abcd").intern();
String d = new String("abcd").intern();
System.out.println(c == d); // Now true
System.out.println(c.equals(d)); // True
3. متى تستخدم علامات الاقتباس المزدوجة ومتى تستخدم المنشئات
نظرًا لأن "abcd" الحرفي دائمًا ما يكون من النوع String، فإن استخدام المُنشئ سيؤدي إلى إنشاء كائن إضافي غير ضروري. لذلك يجب استخدام علامات الاقتباس المزدوجة إذا كنت بحاجة فقط إلى إنشاء سلسلة. إذا كنت تريد بالفعل إنشاء كائن جديد في الكومة، فيجب عليك استخدام المُنشئ. تظهر حالات الاستخدام هنا (الأصلية) . (أقدم النص المترجم أدناه. لكنني ما زلت أوصي بشدة بالتعرف على كود المعلقين على هذا الرابط.)طريقة السلسلة الفرعية () في JDK 6 وJDK 7
طريقة السلسلة الفرعية () في JDK 6 وJDK 7substring(int beginIndex, int endIndex)
بواسطة X Wang تختلف الطريقة في JDK 6 وJDK 7. يمكن أن تساعدك معرفة هذه الاختلافات في استخدام هذه الطريقة بشكل أفضل. ولتسهيل القراءة، substring()
سنقصد أدناه بناء الجملة الكامل، أي. substring(int beginIndex, int endIndex)
.
1. ماذا تفعل السلسلة الفرعية ()؟
تقوم الطريقةsubstring(int beginIndex, int endIndex)
بإرجاع سلسلة تبدأ برقم الحرف beginIndex
وتنتهي برقم الحرف endIndex-1
.
String x = "abcdef";
x = x.substring(1,3);
System.out.println(x);
انتاج:
bc
2. ماذا يحدث عند استدعاء السلسلة الفرعية ()؟
ربما تعلم أنه بسبب الثباتx
، عند تعيين x نتيجة لـ x.substring(1,3)
، x
فإنه يشير إلى صف جديد تمامًا (انظر الرسم البياني): ومع ذلك، هذا الرسم البياني ليس صحيحًا تمامًا؛ لا يوضح ما يحدث بالفعل في الكومة. ما يحدث فعليًا عند الاتصال substring()
يختلف في JDK 6 وJDK 7.
3. سلسلة فرعية () في JDK 6
نوع السلسلة مدعوم بنوع الصفيفchar
. في JDK 6، يحتوي الفصل String
على 3 حقول: char value[]
, int offset
, int count
. يتم استخدامها لتخزين المصفوفة الفعلية من الأحرف، وفهرس الحرف الأول في المصفوفة، وعدد الأحرف في السطر. عندما يتم استدعاء الأسلوب substring()
، فإنه يقوم بإنشاء صف جديد، ولكن قيمة المتغير لا تزال تشير إلى نفس المصفوفة في الكومة. الفرق بين سلسلتين هو عدد الأحرف وقيمة الفهرس لحرف البداية في المصفوفة. الكود أدناه مبسط ويحتوي فقط على الأساسيات لشرح المشكلة.
//JDK 6
String(int offset, int count, char value[]) {
this.value = value;
this.offset = offset;
this.count = count;
}
public String substring(int beginIndex, int endIndex) {
//check boundary
return new String(offset + beginIndex, endIndex - beginIndex, value);
}
4. المشكلة الناجمة عن السلسلة الفرعية () في JDK 6
إذا كان لديك سلسلة طويلة جدًا، لكنك تحتاج فقط إلى جزء صغير منها، والذي تحصل عليه في كل مرة تستخدم فيهاsubstring()
. سيؤدي هذا إلى حدوث مشكلات في التنفيذ، نظرًا لأنك تحتاج فقط إلى جزء صغير، ولكن لا يزال يتعين عليك تخزين السلسلة بأكملها. بالنسبة لـ JDK 6، الحل هو الكود أدناه، والذي سيحول السلسلة إلى سلسلة فرعية حقيقية:
x = x.substring(x, y) + ""
قام المستخدم STepeR بصياغة سؤال (انظر تعليقه)، ويبدو أنه من الضروري إضافة النقطة 4. "المشكلة التي حدثت substring()
في JDK 6" هي مثال أكثر شمولاً. آمل أن يكون هذا هو الجواب وأن يساعد الآخرين على معرفة المشكلة بسرعة. إليك الكود:
String a = "aLongLongString";
String b = a.substring(1, 2);
String c = a.substring(2, 6);
لذا، في JDK 7 b
، فإن الكائنات с
من النوع a String
التي تم إنشاؤها عن طريق استدعاء أسلوب substring()
على كائن من النوع a String
ستشير إلى صفيفين تم إنشاؤهما حديثًا في الكومة - L
for b
, ongL
for c
. سيتم تخزين هذين الصفيفين الجديدين في الكومة مع المصفوفة الأصلية aLongLongString
المشار إليها بواسطة a. أولئك. المصفوفة الأصلية لا تختفي في أي مكان. لنعد الآن إلى JDK 6. في JDK 6، تحتوي الكومة على مصفوفة واحدة aLongLongString
. بعد تنفيذ أسطر التعليمات البرمجية
String b = a.substring(1, 2);
String c = a.substring(2, 6);
b
تشير الكائنات c
إلى نفس المصفوفة في الكومة، المقابلة للكائن a
: b
- إلى العناصر من الفهرس الأول إلى الثاني، c
- إلى العناصر من الفهرس الثاني إلى السادس (يبدأ الترقيم من 0، تذكير). أولئك. من الواضح أن أي وصول إضافي إلى المتغيرات b
أو c في JDK 6 سيؤدي في الواقع إلى "احتساب" العناصر المطلوبة من المصفوفة الأصلية في الكومة. في JDK 7، أي وصول إضافي إلى المتغيرات b
أو لغة c سوف يتسبب في الوصول إلى المصفوفات الأصغر الضرورية التي تم إنشاؤها بالفعل و"المباشرة" في الكومة. أولئك. من الواضح أن JDK 7 يستخدم ذاكرة أكبر فعليًا في مثل هذه المواقف. ولكن دعونا نتخيل خيارًا ممكنًا: يتم تعيين سلاسل فرعية معينة من المتغير للمتغيرات b
، وفي المستقبل يستخدمها الجميع فقط - الكائنات و . لم يعد أحد يصل إلى المتغير ببساطة، ولا توجد إشارات إليه (هذا ما يعنيه كاتب المقال). نتيجة لذلك، في وقت ما، يتم تشغيل أداة تجميع البيانات المهملة، و(في الشكل الأكثر عمومية، بالطبع) نحصل على حالتين مختلفتين: JDK 6 : تم تدمير الكائن (تجميع البيانات المهملة) ، ولكن - المصفوفة الضخمة الأصلية في الكومة حي. يتم استخدامه باستمرار و . JDK 7: تم تدمير الكائن a مع المصفوفة الأصلية في الكومة. هذه هي النقطة في JDK 6 التي يمكن أن تؤدي إلى تسرب الذاكرة. c
a
b
c
a
b
c
5. سلسلة فرعية () في JDK 7
تم تحسين الطريقة في JDK 7. في JDK 7substring()
يقوم بالفعل بإنشاء مصفوفة جديدة على الكومة.
//JDK 7
public String(char value[], int offset, int count) {
//check boundary
this.value = Arrays.copyOfRange(value, offset, offset + count);
}
public String substring(int beginIndex, int endIndex) {
//check boundary
int subLen = endIndex - beginIndex;
return new String(value, beginIndex, subLen);
}
GO TO FULL VERSION