استنساخ القوائم في Kotlin: كيف لا تقع في مشاكل المراجع؟
\n\nتخيل: أنت تكتب تطبيقًا بلغة Kotlin، وتعالج قائمة من البيانات، وفجأة تتغير المصفوفة الأصلية من تلقاء نفسها. ألم مألوف؟ على الأرجح، لقد نسخت المرجع فقط، وليس القائمة نفسها. في هذه المقالة، سنستعرض جميع الطرق القانونية لاستنساخ قائمة في Kotlin — من النسخ السطحي إلى النسخ العميق. بدون حشو، فقط الكود والشروحات.
\n\nالموضوع مستوحى من سؤال شائع على Stack Overflow (أكثر من 200,000 مشاهدة): "How to clone or copy a list in kotlin". دعنا ننهي الأمر مرة واحدة وإلى الأبد ونفهم كيف نسخ قائمة Kotlin بشكل صحيح، حتى لا نفقد البيانات ولا نصاب بالجنون.
\n\nلماذا لا يعمل الإسناد البسيط؟
\n\nفي Kotlin، كما في Java، القوائم هي أنواع مرجعية. إذا كتبت:
\n\nval original = mutableListOf("A", "B", "C")\nval copy = original // هذه ليست نسخة!\ncopy.add("D")\nprintln(original) // [A, B, C, D] — تغيرت الأصلية!\n\nلقد أنشأت ببساطة مرجعًا ثانيًا لنفس الكائن في الذاكرة. أي تغيير عبر copy سينعكس على original. لتجنب ذلك، تحتاج إلى استخدام طرق خاصة لنسخ القائمة في Kotlin.
أفضل 5 طرق لنسخ قائمة في Kotlin
\n\nسنستعرض جميع الخيارات الرئيسية: من toList() البسيط إلى النسخ العميق اليدوي. اختر ما يناسب مهمتك.
1. toList() — الطريقة الأبسط
\n\nالطريقة toList() تنشئ نسخة جديدة غير قابلة للتغيير من القائمة. هذا نسخ سطحي (shallow copy) — العناصر نفسها لا تتكرر، لكن هيكل القائمة يصبح مستقلاً.
val original = listOf("Kotlin", "Java", "Python")\nval copy = original.toList()\n\n// copy هو كائن جديد\nprintln(copy === original) // false — مراجع مختلفة\nprintln(copy == original) // true — محتوى متطابق\n\nمثالي للقوائم البسيطة من السلاسل النصية أو الأرقام. إذا كنت بحاجة إلى نسخة قابلة للتغيير — استخدم toMutableList().
2. toMutableList() — للنسخ القابلة للتغيير
\n\nهل تريد نسخة يمكنك تعديلها؟ toMutableList() هو خيارك. ينشئ MutableList جديد بنفس العناصر.
val original = listOf(1, 2, 3)\nval mutableCopy = original.toMutableList()\nmutableCopy.add(4)\n\nprintln(original) // [1, 2, 3]\nprintln(mutableCopy) // [1, 2, 3, 4] — الأصل لم يتأثر\n\nهذه هي الطريقة الأكثر شيوعًا لاستنساخ mutable list في Kotlin.
\n\n3. ArrayList(collection) — كلاسيكية Java
\n\nإذا كنت قادمًا من Java، ستجد هذه الطريقة مألوفة. المُنشئ ArrayList يأخذ مجموعة أخرى وينسخ عناصرها.
val original = listOf("A", "B", "C")\nval arrayCopy = ArrayList(original)\narrayCopy[0] = "Z"\n\nprintln(original) // [A, B, C]\nprintln(arrayCopy) // [Z, B, C]\n\nيعمل بسرعة ويمكن التنبؤ به. لكن تذكر: هذا أيضًا shallow copy.
\n\n4. map() — مع تحويل العناصر
\n\nإذا كنت لا تريد فقط نسخ القائمة، بل أيضًا تحويل العناصر، استخدم map(). ينشئ قائمة جديدة بناءً على القديمة، بتطبيق دالة لامدا على كل عنصر.
val numbers = listOf(10, 20, 30)\nval doubled = numbers.map { it * 2 }\n\nprintln(numbers) // [10, 20, 30]\nprintln(doubled) // [20, 40, 60]\n\nللنسخ البسيط بدون تغييرات، يمكنك كتابة numbers.map { it }، لكن هذا زائد عن الحاجة — من الأفضل استخدام toList().
5. النسخ العميق (deep copy) للكائنات المعقدة
\n\nجميع الطرق السابقة تقوم بالنسخ السطحي. إذا كانت قائمتك تحتوي على كائنات قابلة للتغيير (مثل data class مع حقول)، فإن تغيير كائن في النسخة سيؤثر على الأصل: