تُعدُّ ArrayList واحدةً من أهم هياكل البيانات في منهج AP Computer Science A، إذ تُمثِّل الأداة الأساسية التي يتعامل معها الطلاب للتعامل مع مجموعات ديناميكية من العناصر في لغة Java. وعلى خلاف المصفوفات التقليدية (arrays) التي يتعين تحديد حجمها مسبقاً، تتميز ArrayList بقدرتها على التوسع والانكماش تلقائياً أثناء التشغيل، مما يجعلها الخيار الأمثل في معظم المواقف التي تتطلب تخزيناً مرناً للبيانات. يتناول هذا الدليل بالتفصيل كل عملية ArrayList المطلوبة في الامتحان، مع أمثلة عملية تستند إلى أنماط الأسئلة المتكررة في اختبارات AP السابقة، لتجاوز هذه الوحدة بثقة تامة.
يركِّز هذا المقال على بناء فهم عميق لكيفية عمل ArrayList من الداخل، لا على حفظ القواعد الآلية فحسب. فالطلاب الذين يدركون المنطق الكامن وراء كل عملية يكونون أكثر قدرةً على تكييف معرفتهم مع الأسئلة غير المألوفة التي قد تظهر في الامتحان. سواءٌ كنت تُراجع للمرة الأولى أو تبحث عن تعميق فهمك قبل موعد الامتحان، ستجد في هذا الدليل ما يُعينك على تحقيق الدرجة الكاملة في هذا المحور.
ما هي ArrayList؟ فهم الفرق الجوهري عن المصفوفات الثابتة
قبل الغوص في العمليات المحددة، من الضروري فهم ماهية ArrayList وموقعها ضمن نظام Java. في لغة Java، تُمثِّل ArrayList صنفاً (class) مُعرَّفاً في حزمة java.util، وليست نوعاً بدائياً مدمجاً في اللغة كما هو الحال مع المصفوفات (int[] أو String[]). هذا يعني أن ArrayList هي في حقيقتها كائن (object) من صنفٍ مُسمًّى ArrayList<E>، حيث يرمز <E> إلى النوع (type) الذي تخزنه القائمة.
تُصرَّح عن ArrayList باستخدام البنية التالية:
ArrayList<Type> name = new ArrayList<Type>();
فمثلاً، لإنشاء قائمة لتخزين الأعداد الصحيحة:
ArrayList<Integer> scores = new ArrayList<Integer>();
ولتخزين سلاسل نصية:
ArrayList<String> names = new ArrayList<String>();
هنا يجب الانتباه إلى نقطة جوهرية: عند تحديد نوع العناصر المخزَّنة في ArrayList، يُجب استخدام wrapper class للأنواع البدائية — أي Integer بدلاً من int، وDouble بدلاً من double، وBoolean بدلاً من boolean. السبب في ذلك أن ArrayList مصمَّمة لتخزين الكائنات (objects) فقط، وليس الأنواع البدائية. هذه النقطة تظهر في أسئلة AP بصفتها سبباً شائعاً لأخطاء الترجيح (type mismatch errors) يُعاني منها الطلاب.
أما الفرق الأساسي بين ArrayList والمصفوفات، فيتمثل في أن حجم المصفوفة يُحدَّد عند إنشائها ولا يتغير، بينما يمكن إضافة عناصر إلى ArrayList أو إزالتها منها دون قيود محددة عند 선언ها. هذا لا يعني أن ArrayList أفضل دائماً — فالمصفوفات تظل أكثر كفاءةً في الذاكرة والسرعة عندما يكون الحجم معروفاً مسبقاً ويتطلَّب الوصول العشوائي المتكرر — لكن في سياق الامتحان، تُستخدَم ArrayList حين يكون العدد المتوقع من العناصر متغيراً أو غير محدد مسبقاً.
العمليات الأساسية: الإضافة والحذف والتعديل
تغطي هذه الوحدة أربع عمليات أساسية تُشكِّل العمود الفقري لأي سؤال متعلق بـ ArrayList في امتحان AP Computer Science A. يُنصح بحفظ توقيع كل عملية (method signature) بدقة، إذ أن أي اختلاف في الاسم أو المعاملات يُنتج خطأً في الترجيح أو سلوكاً غير متوقع.
إضافة عنصر: add()
تُستخدم عملية add() لإضافة عنصر إلى نهاية القائمة في الحالة الأكثر شيوعاً:
names.add("Ali");
يمكن أيضاً إدراج عنصر في موضع محدد (index) باستخدام الصيغة:
names.add(0, "Ali"); // يُضيف "Ali" في بداية القائمة
في صيغة الموضع المحدود، يجب أن يكون الفهرس (index) صالحاً — أي بين الصفر وطول القائمة الحالي. عند إدراج عنصر في موضع وسط، تتم إعادة ترتيب جميع العناصر اللاحقة بمقدار موقع واحد نحو اليمين، مما يعني أن الفهرس يزداد لكل عنصر موجود بعد نقطة الإدراج.
الوصول إلى عنصر: get()
تُعيد عملية get(index) العنصر الموجود في الفهرس المُحدَّد:
String first = names.get(0); // يُعيد العنصر الأول
يجب أن يكون الفهرس ضمن النطاق الصحيح (من صفر إلى size() - 1)، وإلا سيُرمى استثناء IndexOutOfBoundsException. هذه حقيقةٌ مهمة تُختبر كثيراً في أسئلة الامتحان: فهم أن الفهرس الأخير الصحيح هو size() - 1 وليس size().
تعديل عنصر: set()
تُبدِّل عملية set(index, element) العنصر الموجود في الفهرس المُحدَّد بعنصر جديد:
names.set(0, "Sara"); // يُبدِّل العنصر الأول بـ "Sara"
تُعيد هذه العملية العنصر القديم الذي تم استبداله، وهي نقطة قد تُفيد في بعض الأسئلة المتقدمة. لكن الأهم هو أن هذه العملية لا تُغيِّر حجم القائمة — فقط تُحدِّث قيمة عنصر قائم.
حذف عنصر: remove()
توجد صيغتان لعملية الحذف:
names.remove(0); // يحذف العنصر الموجود في الفهرس 0
names.remove("Ali"); // يحذف أول ظهور للعنصر "Ali"
عند حذف عنصر من وسط القائمة، تتم إعادة ترتيب جميع العناصر اللاحقة بمقدار موقع واحد نحو اليسار. تُعيد الصيغة الأولى العنصر المحذوف، بينما تُعيد الصيغة الثانية قيمة منطقية (boolean) تُشير إلى نجاح العملية.
البحث والفرز: indexOf و contains و sort
يُعدُّ البحث داخل ArrayList من أكثر المهارات المطلوبة في الامتحان، سواءٌ كجزء من أسئلة فرعية في وحدة.classes أو كعنصر رئيسي في أسئلة تحليل السجل (Free Response Questions). هناك ثلاث عمليات أساسية للبحث يجب إتقانها:
indexOf() - موقع العنصر
تُعيد عملية indexOf(element) فهرس أول ظهور للعنصر المُحدَّد، أو -1 إذا لم يُعثر عليه:
int pos = names.indexOf("Ali"); // يُعيد 2 إذا وُجد "Ali" في الفهرس 2
تُعدُّ قيمة -1 المعيار الشائع للتحقق من وجود عنصر في القائمة، وهي تُختبر كثيراً في عبارات الشرط (if statements). الممارسة المعتادة:
if (names.indexOf("Ali") != -1) { // Ali موجود في القائمة }
contains() - التحقق من الوجود
تُعيد عملية contains(element) قيمة منطقية (boolean):
boolean found = names.contains("Sara"); // تُعيد true أو false
هذه العملية底层 تستدعي indexOf() فعلياً، لكنها أكثر وضوحاً وقراءةً في الكود. يُفضَّل استخدامها في عبارات الشرط بدلاً من الاعتماد على indexOf() إلا عند الحاجة إلى معرفة موقع العنصر بالتحديد.
Collections.sort() - الفرز
تُستخدم عملية Collections.sort(list) لفرز قائمة تصاعدياً. بما أن هذه العملية مُعرَّفة في صنف Collections وليست في ArrayList مباشرةً، يجب استيراد الحزمة java.util.Collections:
import java.util.Collections;
Collections.sort(names); // يفرز القائمة أبجدياً
لاحظ أن عملية الفرز هذه تُعدِّل القائمة الأصلية نفسها (in-place)، أي أنها لا تُنشئ قائمة جديدة بل تُعيد ترتيب العناصر داخل القائمة الحالية.
التكرار على ArrayList: ثلاث طرق للامتحان
التكرار (iteration) على عناصر ArrayList موضوعٌ متكرر في أسئلة AP Computer Science A، ويظهر بثلاث صيغ مختلفة يجب على كل مرشح إتقانها. يعتمد اختيار الصيغة المناسبة على طبيعة السؤال — هل المطلوب الوصول إلى العنصر فقط، أم إلى فهرسه أيضاً، أم تعديله أثناء التكرار؟
الصيغة الأولى: حلقة for بالموقع
هذه الصيغة الأكثر مرونةً وتُستخدم حين الحاجة إلى الفهرس:
for (int i = 0; i < scores.size(); i++) { System.out.println(scores.get(i)); }
يُناسب هذا النمط المواقف التي تتطلب مقارنة كل عنصر بعنصر آخر في القائمة، أو إجراء تعديلات مبنية على الموقع.
الصيغة الثانية: حلقة for-each المحسَّنة
صيغة أنظف وأقصر، مثالية حين يكفي الوصول إلى قيمة العنصر:
for (String name : names) { System.out.println(name); }
لا تُتيح هذه الصيغة الوصول المباشر إلى الفهرس، لكنها أكثر قراءةً وتقلل احتمالات الأخطاء. في الامتحان، إذا كان السؤال يتطلب طباعة جميع العناصر أو التحقق من شرط معين لكل عنصر، فإن for-each غالباً ما تكون الخيار الأنسب.
الصيغة الثالثة: حلقة while مع مُكرر
تُستخدم حين يكون الشرط.stopping معقداً أو يعتمد على منطق خارج نطاق الحلقة:
int i = 0; while (i < scores.size()) { if (scores.get(i) < 0) { break; } i++; }
من الأخطاء الشائعة في هذا السياق: تعديل حجم القائمة أثناء التكرار بحلقات for-each أو while باستخدام عمليات add() أو remove(). هذا قد يُنتج استثناءً ConcurrentModificationException أو سلوكاً غير متوقع. القاعدة العامة: إذا احتجت إضافة عناصر أو حذفها أثناء التكرار، استخدم حلقة for بالموقع وعدِّل حجم القائمة بحذر شديد.
ArrayList مع الكائنات: تخزين عناصر معقدة
في معظم أسئلة AP الحقيقية، لا تُستخدَم ArrayList لتخزين أنواع بدائية أو سلاسل نصية بسيطة، بل لتخزين كائنات (objects) مُعرَّفة من قبل الطالب. هذا يتوافق مع المحور الأكبر للمنهج — البرمجة كائنية التوجه (Object-Oriented Programming) — ويُمثِّل تحدياً إضافياً يتطلب فهماً متكاملاً لكلٍّ من ArrayList والأصناف.
لنتأمل مثالاً كلاسيكياً يظهر في اختبارات AP:
ArrayList<Dog> kennel = new ArrayList<Dog>(); kennel.add(new Dog("Max", 3)); kennel.add(new Dog("Bella", 5)); for (Dog d : kennel) { System.out.println(d.getName()); }
في هذا المثال، تخزن ArrayList كائنات من صنف Dog. عند التكرار، يُستدعى أسلوب الوصول getName() لكل كائن — وهذا يفترض أن الصنف Dog يحتوي على هذا الأسلوب (public String getName()).
من المهارات المطلوبة في هذا السياق:
- فرز قائمة كائنات: يتطلب استخدام
Collections.sort()مع شرط أن الصنف المُخزَّن يُنفِّذ واجهةComparable، أو توفير كائنComparatorخارجي. هذا الموضوع يتجاوز نطاق امتحان AP Computer Science A القياسي، لكنه يظهر في بعض الأسئلة المتقدمة. - البحث عن كائن معيّن: يتطلب استخدام حلقة تكرار أو طريقة
indexOf()التي تعتمد على أسلوبequals()المُعَرَّف في الصنف. الصنف должен implement метод equals() правильно, иначе indexOf() будет использовать ссылочное равенство (==), что может привести к неправильным результатам.
أخطاء ArrayList الأكثر شيوعاً في الامتحان
من خلال تحليل أسئلة AP Computer Science A السابقة، يمكن تحديد مجموعة من الأخطاء المتكررة التي تُكلِّف الطلاب درجات ثمينة. تجنب هذه الأخطاء يتطلب فهماً عميقاً لا مجرد حفظ.
خطأ الأنواع: استخدام النوع البدائي
أحد أكثر الأخطاء شيوعاً في الامتحان:
ArrayList<int> numbers = new ArrayList<int>(); // خطأ: int ليس كائناً!
الحل الصحيح:
ArrayList<Integer> numbers = new ArrayList<Integer>();
هذا الخطأ يُنتج خطأً في الترجيح (compile-time error)، مما يعني أن البرنامج لن يعمل أصلاً. السؤال عادةً لا يطلب تحديد الخطأ الظاهر في الكود، بل يطلب التنبؤ بالمخرجات أو اختيار التصحيح الأنسب — وهنا يظهر هذا الخطأ كثيراً كخيار مُغري.
خطأ فهرس خارج النطاق
الوصول إلى فهرس لا ينتمي إلى القائمة:
ArrayList<String> list = new ArrayList<String>(); list.add("A"); list.add("B"); System.out.println(list.get(2)); // خطأ: الفهرس 2 غير موجود
الفهارس الصالحة في هذه الحالة هي 0 و 1 فقط (لأن size() = 2). الوصول إلى الفهرس 2 يُنتج استثناء IndexOutOfBoundsException أثناء التشغيل. في أسئلة التنبؤ بالمخرجات، يجب الانتباه إلى أن هذا الاستثناء يتسبب في إنهاء البرنامج وعرض رسالة خطأ، وليس إرجاع قيمة فارغة.
عدم تهيئة القائمة
من الأخطاء المنطقية (logical errors) التي لا يُكتشفها المُترجِم:
ArrayList<String> list; System.out.println(list.size()); // خطأ: المتغير غير مُهيَّأ
هذا الكود يُنتج خطأً في الترجيح لأن المتغير لم يتم تهيئته. لكن في أسئلة الامتحان، قد يظهر كمتغير مُهيَّأ داخل المُنشئ (constructor) أو طريقة (method)، والحكم على القيمة الأولية لـ size() يختلف حسب السياق.
عدم استيراد الحزمة
إذا لم يتم استيراد java.util.ArrayList أو java.util.Collections، سيُنتج المُترجِم خطأً بعدم وجود الصنف. في أسئلة AP، عادةً ما تُوفَّر القائمة المستوردة في بداية الكود، لكن إذا طُرح سؤال يتطلب استخدام Collections.sort()، يجب التأكد من توفر الاستيراد.
ArrayList مقابل المصفوفات: متى تستخدم كلاً منهما؟
يُعدُّ هذا السؤال من أكثر أسئلة المقارنة تكراراً في قسم الاختيار من متعدد (Multiple-Choice Section)، إذ يُطلب من الطالب تحديد أيهما الأنسب لموقف مُعطًى. الجدول التالي يُلخِّص الفروقات الجوهرية:
| المعيار | ArrayList | المصفوفة (Array) |
|---|---|---|
| حجم القائمة | ديناميكي — ينمو ويَنكمش تلقائياً | ثابت — يُحدَّد عند الإنشاء ولا يتغير |
| نوع العناصر | كائنات فقط (يستخدم wrapper classes) | أي نوع بما فيها البدائية |
| الأداء (وصول عشوائي) | O(1) — سريع | O(1) — أسرع قليلاً |
| الأداء (إدراج/حذف) | أبطأ — يتطلب إعادة ترتيب العناصر | أسرع نسبياً في التعديل المباشر |
| الطول | list.size() | array.length |
| الفهرس الأخير | size() - 1 | length - 1 |
| الأسلوب get() | list.get(i) | array[i] |
| في امتحان AP | تُستخدم حين يكون الحجم متغيراً | تُستخدم حين يكون الحجم ثابتاً ومعروفاً |
القاعدة الإرشادية للامتحان: إذا وصف السؤال موقفاً يتطلب تخزين عدد من العناصر غير محدد مسبقاً — كقائمة طلاب أو سجلات درجات أو عناصر تُضاف تدريجياً — فإن ArrayList هي الخيار الأنسب. أما إذا ذُكر أن عدد العناصر ثابت ومعروف مسبقاً — كتخزين أيام الأسبوع السبعة أو نتائج 10 اختبارات — فإن المصفوفة التقليدية تكفي وتكون أكثر كفاءةً.
تحليل أنماط أسئلة الامتحان على ArrayList
تتوزع أسئلة ArrayList في امتحان AP Computer Science A على نمطين رئيسيين: أسئلة الاختيار من متعدد (multiple-choice questions) وأسئلة التحليل الحر (free-response questions). لكل نمط خصائصه التي يجب مراعاتها.
أسئلة الاختيار من متعدد
في هذا النمط، يظهر ArrayList عادةً ضمن سياق كود أطول يُطلب من الطالب تحليله. الأسئلة الشائعة تشمل:
- التنبؤ بالمخرجات: يُعطَى كود يحتوي على عمليات ArrayList ويُطلب تحديد ما سيُطبع. هنا يجب تنفيذ الكود سطراً بسطر مع تتبع حالة القائمة بعد كل عملية.
- تحديد الخطأ: يُعطَى كود يحوي خطأً ويُطلب اختيار التصحيح المناسب. الأخطاء النموذجية: استخدام نوع بدائي، فهرس خارج النطاق، أو تهيئة خاطئة.
- اختيار الأفضل (most appropriate): يُوصف موقف ويطلب تحديد أي بنية بيانات هي الأنسب. يجب تطبيق القاعدة الإرشادية: حجم متغير → ArrayList، حجم ثابت → مصفوفة.
أسئلة التحليل الحر (Free Response)
في هذا النمط، يُطلب من الطالب كتابة كود يُنفِّذ متطلبات محددة. أسئلة ArrayList تظهر عادةً متداخلة مع أصناف (classes) ومفاهيم OOP أخرى:
- كتابة طريقة تبحث في ArrayList: مثل: «اكتب طريقة تُعيد عدد الطلاب الذين حصلوا على درجة أعلى من 90».
- التعامل مع ArrayList من كائنات: مثل: «اكتب طريقة تُعيد الطالب صاحب أعلى درجة من قائمة Students».
- تعديل ArrayList: مثل: «اكتب طريقة تُزيل جميع الطلاب الذين تقل درجاتهم عن 60 من القائمة».
في أسئلة التحليل الحر، يُقيَّم الكود وفق معايير محددة (rubric) تشمل: صحة البنية، استخدام البنية الصحيحة، والتعامل مع الحالات الحدية. المصفوفات و ArrayList متكافئتان تقريباً في التقييم، لكن إذا كانت الأداة المطلوبة في السؤال هي ArrayList تحديداً، فيجب استخدامها.
الخلاصة والخطوات التالية
تُعدُّ ArrayList أداةً أساسية لا غنى عنها في AP Computer Science A، لكنها ليست معزولة — بل تتكامل مع مفاهيم الأصناف والتوثيق (encapsulation) والميراث (inheritance) الذي تناوله دليلٌ آخر في هذه السلسلة. المفتاح للنجاح يكمن في ثلاثة أمور: فهم الفرق بين ArrayList والمصفوفات ومتى يُستخدم كل منهما، إتقان توقيعات العمليات الأربع الأساسية (add و get و set و remove)، والتدرب على التكرار بأنماطه الثلاثة حتى يصير تلقائياً.
كخطوة أولى، يُنصح بحل مجموعة من أسئلة الاختيار من متعدد المُتعلقة بـ ArrayList من اختبارات AP السابقة، مع التركيز على الأسئلة التي تجمع بين ArrayList والأصناف المُعرَّفة من المستخدم. ثم الانتقال إلى أسئلة التحليل الحر التي تتطلب كتابة كود يُحقق متطلبات محددة باستخدام ArrayList، مع تطبيق معايير التقييم ذاتها التي يستخدمها المصححون.
يوفر التقييم المبدئي المجاني من TestPrep نقطة انطلاق مثالية لتقييم مستوى الاستعداد في هذه الوحدة وتلقي خطة دراسة مُخصَّصة تُناسب المرحلة الحالية من التحضير.