StringBuilder مقابل StringBuffer: 10 اختلافات رئيسية في Java

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

StringBuilder مقابل StringBuffer: ماذا تختار ولماذا هذا مهم؟

\n\n

إذا كنت قد كتبت يومًا كودًا بلغة Java يعمل بكثافة مع السلاسل النصية (مثل تجميع استعلام SQL، أو إنشاء JSON، أو مجرد لصق عشرات القطع النصية)، فمن المحتمل أنك واجهت معضلة: StringBuilder أم StringBuffer؟ على Stack Overflow، حصد هذا السؤال مئات الآلاف من المشاهدات، وليس بدون سبب. للوهلة الأولى، تبدو الفئتان متشابهتين كتوأمين - كلاهما يوسع AbstractStringBuilder، وكلاهما يستخدم مصفوفة أحرف قابلة للتغيير، وكلاهما يدعم append() و insert() و delete(). لكن الشيطان، كما هو الحال دائمًا، يكمن في التفاصيل.

\n\n

في هذه المقالة، لن نكتفي بسرد الاختلافات. سنقوم بتحليلها بأمثلة واقعية، ونلقي نظرة تحت غطاء JVM، ونجيب على الأسئلة التي سيُطرح عليك بالتأكيد في مقابلات العمل. هيا بنا!

\n\n

1. الفرق الرئيسي: المزامنة

\n\n

هذه هي النقطة الأهم، والتي من أجلها، في الواقع، تم إثارة كل هذه الضجة. StringBuffer آمن للاستخدام في بيئة متعددة الخيوط (thread-safe). جميع طرقه العامة تقريبًا متزامنة باستخدام الكلمة المفتاحية synchronized. StringBuilder غير متزامن. هذا كل شيء.

\n\n

دعنا نلقي نظرة على الكود (نسخة مبسطة من JDK الفعلي):

\n\n
// StringBuffer (Java 17)\npublic synchronized StringBuffer append(String str) {\n    toStringCache = null;\n    super.append(str);\n    return this;\n}\n\n// StringBuilder\npublic StringBuilder append(String str) {\n    super.append(str);\n    return this;\n}\n
\n\n

سطر واحد synchronized - وهذا هو كل الفرق. لكن هذا السطر يغير كل شيء: الأداء، مجال الاستخدام، وحتى بنية التطبيق.

\n\n

متى تستخدم StringBuffer؟

\n
    \n
  • عند كتابة تطبيق متعدد الخيوط.
  • \n
  • عند استخدام نفس النسخة من StringBuffer من عدة خيوط بدون مزامنة خارجية.
  • \n
  • مثال: مسجل (logger) يجمع الرسائل من خيوط مختلفة في مخزن مؤقت واحد.
  • \n
\n\n

متى تستخدم StringBuilder؟

\n
    \n
  • الكود أحادي الخيط (90% من الحالات).
  • \n
  • المتغيرات المحلية داخل الدالة.
  • \n
  • الحلقات التكرارية مع دمج السلاسل (المترجم سيقترح StringBuilder بنفسه).
  • \n
\n\n

2. الأداء: من الأسرع؟

\n\n

كل شيء متوقع هنا: StringBuilder أسرع من StringBuffer. بكم؟ يعتمد على الحمل، لكن في المتوسط من 2 إلى 5 مرات. دعنا نتحقق من ذلك باختبار أداء بسيط.

\n\n
public class PerformanceTest {\n    private static final int ITERATIONS = 10_000_000;\n\n    public static void main(String[] args) {\n        // StringBuilder\n        long start = System.nanoTime();\n        StringBuilder sb = new StringBuilder();\n        for (int i = 0; i < ITERATIONS; i++) {\n            sb.append("a");\n        }\n        long sbTime = System.nanoTime() - start;\n\n        // StringBuffer\n        start = System.nanoTime();\n        StringBuffer sbf = new StringBuffer();\n        for (int i = 0; i < ITERATIONS; i++) {\n            sbf.append("a");\n        }\n        long sbfTime = System.nanoTime() - start;\n\n        System.out.println("StringBuilder: " + sbTime / 1_000_000 + " ms");\n        System.out.println("StringBuffer:  " + sbfTime / 1_000_000 + " ms");\n        System.out.println("Buffer أبطأ بمقدار " + (sbfTime / (double) sbTime) + " مرة");\n    }\n}\n
\n\n

على جهاز الكمبيوتر المحمول الخاص بي (Intel i7، 16GB RAM، Java 17)، النتيجة ثابتة: StringBuilder ~180 مللي ثانية، StringBuffer ~620 مللي ثانية. الفرق يقارب 3.5 مرات. وإذا أضفنا عملاً حقيقيًا (تحليل، تنسيق)، قد يزداد الفارق. لماذا؟ لأن كل استدعاء لـ synchronized هو فحص للشاشة (monitor)، وأخذ قفل (lock) وتحريره. في الكود أحادي الخيط، هذا مضيعة للوقت الخالص.

\n\n
\n هام: إذا كانت طريقتك تضمن

المدونات

توصيات الكتب