أعتقد أن كل شخص لديه فهم جيد للفرق بين String و StringBuffer، ولكن من المقدر أنه لا يزال هناك العديد من الرفاق الذين ليس لديهم فكرة واضحة عن مبادئ عمل هاتين الفئتين، وسأقوم اليوم بمراجعة هذا المفهوم للجميع، ومن خلال الطريقة: تم تقديم فئة تشغيل شخصية جديدة في J2SE 5.0 - StringBuilder. إذن ما هي الاختلافات بين StringBuilder و StringBuffer وفئة String التي التقينا بها لأول مرة؟ أيهما يجب أن نستخدمه في المواقف المختلفة؟ أود أن أشارككم آرائي حول هذه الفئات، وآمل أيضًا أن يتمكن الجميع من إبداء آرائهم، لقد ارتكب الجميع أخطاء، وأثناء تصحيحها، فهي فرصة جيدة للتعلم.
باختصار، الفرق الرئيسي في الأداء بين نوع String ونوع StringBuffer هو في الواقع أن String كائن غير قابل للتغيير (لماذا؟ اسأل مصممي Java، لماذا لا تعتبر String نوعًا أصليًا؟) لذلك، في كل مرة يتم فيها تغيير نوع String في الواقع، إنه يعادل إنشاء كائن سلسلة جديد، ثم توجيه المؤشر إلى كائن السلسلة الجديد، لذلك من الأفضل عدم استخدام السلسلة للسلاسل التي غالبًا ما تغير المحتوى. ، لأنه في كل مرة يتم فيها إنشاء كائن، سيكون له تأثير على أداء النظام. خاصة عندما يكون هناك عدد كبير جدًا من الكائنات غير المرجعية في الذاكرة، سيبدأ GC الخاص بـ JVM في العمل، وستكون السرعة بطيئة جدًا بالتأكيد. وإليك مثالاً غير مناسب جدًا:
إذا كان هذا هو الحال، بعد اكتمال حلقة for، إذا لم يتم مسح الكائنات الموجودة في الذاكرة بواسطة GC، فسيكون هناك أكثر من 20000 في الذاكرة، وهو رقم مذهل، وإذا كان هذا نظامًا يستخدمه الكثيرون الناس، فالعدد ليس كبيرًا جدًا، لذا يجب على الجميع توخي الحذر عند استخدامه.
إذا كنت تستخدم فئة StringBuffer، فستكون النتائج مختلفة في كل مرة ستكون النتيجة عبارة عن عملية على كائن StringBuffer نفسه، بدلاً من إنشاء كائن جديد ثم تغيير مرجع الكائن. لذا نوصي بشكل عام باستخدام StringBuffer، خاصة عندما تتغير كائنات السلسلة بشكل متكرر. في بعض الحالات الخاصة، يتم تفسير تسلسل كائنات السلسلة فعليًا بواسطة JVM على أنه تسلسل لكائنات StringBuffer، لذلك في هذه الحالات لن تكون سرعة كائنات السلسلة أبطأ من سرعة كائنات StringBuffer، وخاصة كائنات السلسلة التالية من بينها، كفاءة السلسلة أسرع بكثير من StringBuffer:
سوف تتفاجأ عندما تجد أن سرعة إنشاء كائنات String S1 هي ببساطة سريعة جدًا، وفي هذا الوقت ليس لدى StringBuffer أي ميزة على الإطلاق في السرعة. في الواقع، هذه خدعة JVM في نظر JVM
من هذا نحصل على استنتاج الخطوة الأولى: في معظم الحالات StringBuffer > String
وكيف يقارن StringBuilder معهم؟ اسمحوا لي أن أقدمها بإيجاز أولاً. StringBuilder هي فئة تمت إضافتها حديثًا في JDK5.0. الفرق بينها وبين StringBuffer هو كما يلي (المصدر: JavaWorld):
Java.lang.StringBuffer تسلسل أحرف قابل للتغيير آمن. مخزن مؤقت لسلسلة يشبه String، لكن لا يمكن تعديله. يمكن استخدام المخازن المؤقتة للسلسلة بأمان بواسطة مؤشرات ترابط متعددة. يمكن مزامنة هذه الأساليب عند الضرورة، بحيث تظهر جميع العمليات على أي مثيل معين وكأنها تحدث بترتيب تسلسلي يتوافق مع ترتيب استدعاءات الأساليب التي يتم إجراؤها بواسطة كل مؤشر ترابط معني.
كل مخزن مؤقت للسلسلة له سعة معينة. طالما أن طول تسلسل الأحرف الموجود في المخزن المؤقت للسلسلة لا يتجاوز هذه السعة، ليست هناك حاجة لتخصيص مصفوفة مخزن مؤقت داخلي جديد. تتم زيادة هذه السعة تلقائيًا في حالة تجاوز سعة المخزن المؤقت الداخلي. بدءًا من JDK 5.0، تمت إضافة فئة مكافئة لاستخدام مؤشر ترابط واحد، StringBuilder، إلى هذه الفئة. يجب بشكل عام استخدام فئة StringBuilder بدلاً من هذه الفئة لأنها تدعم جميع العمليات نفسها ولكنها أسرع لأنها لا تقوم بالمزامنة.
ولكن من غير الآمن استخدام مثيل StringBuilder مع عدة سلاسل رسائل. إذا كانت هذه المزامنة مطلوبة، فمن المستحسن استخدام StringBuffer.
مع هذا، أعتقد أن الجميع يمكن أن يفهموا الفرق بينهما، لذلك دعونا نستنتج اشتقاقًا عامًا أدناه:
في معظم الحالات StringBuilder > StringBuffer
لذلك، وفقًا للنظرية المتعدية لهذه عدم المساواة: في معظم الحالات StringBuilder > StringBuffer > String
والآن بعد أن حصلنا على نتائج الاشتقاق هذه، فلنجري اختبارًا للتحقق:
رمز الاختبار هو كما يلي:
/** إنشاء مثيل جديد للاختباراتsb */
Final static int ttime = 10000;// عدد حلقات الاختبار
الاختبارات العامة () {
}
اختبار الفراغ العام (سلسلة) {
بداية طويلة = System.currentTimeMillis();
ل(int i=0;i<ttime;i++){
ق += "أضف";
}
فترة طويلة = System.currentTimeMillis();
System.out.println("الوقت الذي تستخدمه العملية "+s.getClass().getName()+" هو: " + (over - begin) + " مللي ثانية" );
}
اختبار الفراغ العام (StringBuffer s) {
بداية طويلة = System.currentTimeMillis();
ل(int i=0;i<ttime;i++){
s.append("add");
}
فترة طويلة = System.currentTimeMillis();
System.out.println("الوقت الذي تستخدمه العملية "+s.getClass().getName()+" هو: " + (over - begin) + " مللي ثانية" );
}
اختبار الفراغ العام (StringBuilder s) {
بداية طويلة = System.currentTimeMillis();
ل(int i=0;i<ttime;i++){
s.append("add");
}
فترة طويلة = System.currentTimeMillis();
System.out.println("الوقت الذي تستخدمه العملية "+s.getClass().getName()+" هو: " + (over - begin) + " مللي ثانية" );
}
// اختبار تسلسل السلسلة مباشرة على String
اختبار الفراغ العام 2 () {
سلسلة s2 = "أبادف";
بداية طويلة = System.currentTimeMillis();
ل(int i=0;i<ttime;i++){
سلسلة s = s2 + s2 + s2 ;
}
فترة طويلة = System.currentTimeMillis();
System.out.println("الوقت المستخدم لتشغيل نوع إضافة مرجع كائن السلسلة هو: " + (over - begin) + "ميلي ثانية")؛
}
اختبار الفراغ العام 3 () {
بداية طويلة = System.currentTimeMillis();
ل(int i=0;i<ttime;i++){
String s = "abadf" + "abadf" + "abadf" ;
}
فترة طويلة = System.currentTimeMillis();
System.out.println("الوقت المستخدم لإضافة سلاسل العمليات هو: "+ (over - begin) + "ميلي ثانية")؛
}
public static void main(String[] args){
سلسلة s1 = "اي بي سي"؛
StringBuffer sb1 = new StringBuffer("abc");
StringBuilder sb2 = new StringBuilder("abc");
testssb t = new testsb();
t.test(s1);
t.test(sb1);
t.test(sb2);
t.test2();
t.test3();
}
}
يبدو أنك لا تزال غير قادر على رؤية الفرق بين StringBuffer وStringBuilder، أضف ttime إلى 30000 مرة وانظر:
الوقت المستخدم لتشغيل النوع java.lang.String هو: 53444 مللي ثانية الوقت المستخدم لتشغيل النوع java.lang.StringBuffer هو: 15 مللي ثانية الوقت المستخدم لتشغيل النوع java.lang.StringBuilder هو: 15 مللي ثانية الوقت يستخدم لتشغيل نوع إضافة مرجع كائن السلسلة الوقت المستغرق: 31 مللي ثانية يستغرق إضافة السلاسل وقتًا: 0 مللي ثانية
لا يزال هناك فرق كبير في الأداء بين StringBuffer وStringBuilder، دعنا نزيده إلى 100000 ونلقي نظرة. لن نضيف اختبارًا لنوع السلسلة هنا، لأن اختبار مثل هذه الكمية الكبيرة من البيانات لنوع السلسلة سيكون أمرًا صعبًا. بطيء جدا...
الوقت المستغرق لتشغيل النوع java.lang.StringBuffer هو: 31 مللي ثانية الوقت المستغرق لتشغيل النوع java.lang.StringBuilder هو: 16 مللي ثانية
يمكنك رؤية الفرق، لكن العديد من نتائج الاختبار تظهر أن StringBuffer أسرع من StringBuilder، دعنا نزيد القيمة إلى 1000000 ونرى (لا ينبغي أن يتعطل، أليس كذلك؟):
الوقت المستغرق لتشغيل النوع java.lang.StringBuffer هو: 265 مللي ثانية الوقت المستغرق لتشغيل النوع java.lang.StringBuilder هو: 219 مللي ثانية
هناك اختلافات أقل، والنتائج مستقرة جدًا، فلننظر إلى الأمر بشكل أكبر قليلاً، ttime = 5000000:
・・・・・・ استثناء في سلسلة المحادثات "الرئيسية" java.lang.OutOfMemoryError: مساحة كومة Java ・・・・・・
هاها، انسَ الأمر، لن أختبره بعد الآن، الأداء في الأساس هو StringBuilder > StringBuffer > String.