المصفوفة قائمة ذات حجم ثابت
تحتفظ المصفوفة في جافا بعدد ثابت من القيم من النوع نفسه، مرتبةً بالتتابع ويُوصَل إليها بالموضع. والكلمة المفتاحية هي "ثابت": بمجرد أن تنشئ مصفوفة، لا يتغير طولها أبدًا. و"النوع نفسه": فالمصفوفة int[] لا تحمل سوى قيم int، والمصفوفة String[] لا تحمل سوى السلاسل النصية. هذه الصرامة هي الثمن الذي تدفعه مقابل أن تكون المصفوفات سريعة ومُوفِّرة للذاكرة.
تعلن عن نوع مصفوفة بوضع [] بعد نوع العنصر، ثم إما أن تُسند قيمة حرفية أو تخصّص واحدة باستخدام new:
يمكن أن توضع الأقواس المربعة بعد النوع (int[] scores) أو بعد الاسم (int scores[]). كلاهما يُصرَّف، لكن int[] scores هو الأسلوب الاصطلاحي في جافا - ضع [] مع النوع، حيث ينبغي أن تكون.
إنشاء مصفوفة باستخدام new
عندما لا تعرف القيم بعدُ لكنك تعرف الحجم، استخدم new. فهذا يخصّص الخانات ويملؤها بالقيمة الافتراضية للنوع:
القيم الافتراضية ليست خردة عشوائية - بل تُهيّئ جافا كل عنصر بالصفر. تبدأ الأنواع العددية من 0 (أو 0.0)، ويبدأ النوع boolean من false، وتبدأ الأنواع المرجعية مثل String من null. ويجدر تذكّر هذا الـnull: فالمصفوفة String[] المخصَّصة حديثًا مملوءة بـnull، واستدعاء دالة على أحدها يطلق NullPointerException.
تبدأ الفهرسة من الصفر
تقرأ العناصر وتكتبها بالفهرس، بدءًا من 0. والفهرس الصالح الأخير هو دائمًا length - 1:
وعلى خلاف المصفوفات في بعض لغات السكربتة، فإن تجاوز النطاق لا يعطيك بصمتٍ null أو undefined - بل يطلق استثناءً. جرّب قراءة فهرس غير موجود:
ينهار ذلك البرنامج مُطلقًا:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 3
هذا من أكثر أخطاء المبتدئين شيوعًا. والإصلاح يكاد يكون دائمًا خطأ الانزياح بمقدار واحد (off-by-one) في شرط حلقة - استخدام <= حيث كنت تقصد <.
length حقل وليس دالة
تحمل كل مصفوفة حجمها في حقل length. لاحظ: بدون أقواس.
وإليك مزلقًا يقع فيه الجميع مرةً على الأقل: تستخدم المصفوفات .length (حقل)، لكن String يستخدم .length() (دالة). فكتابة nums.length() على مصفوفة خطأ تصريف، وكتابة text.length على سلسلة نصية خطأ كذلك. الكلمة نفسها بقواعد مختلفة - والذي يخص المصفوفة بلا أقواس.
التكرار على مصفوفة
تعلّمت حلقة for-each في الصفحة السابقة، وهي أنظف طريقة لقراءة كل عنصر عندما لا تحتاج إلى الفهرس:
وعندما تحتاج إلى الفهرس - لطباعة أرقام المواضع، أو تعديل العناصر في مكانها، أو التكرار رجوعًا - فالجأ إلى حلقة for المفهرسة التقليدية مستخدمًا length حدًّا:
الشرط هو i < names.length، وليس <=. فاستخدام <= يتقدّم خطوة زائدة ويطلق استثناء تجاوز النطاق الوارد في القسم أعلاه. قاعدة سريعة: استخدم for-each حين تقرأ القيم فقط، وحلقة for المفهرسة حين تحتاج إلى الموضع أو تريد الكتابة في المصفوفة.
المصفوفات متعددة الأبعاد
المصفوفة ثنائية الأبعاد هي في الحقيقة مصفوفة من المصفوفات - تخيّلها شبكةً من الصفوف والأعمدة. تضيف زوجًا ثانيًا من الأقواس المربعة:
يختار grid[الصف][العمود] خليةً واحدة. ولأن كل صف مصفوفةٌ بذاته، فإن grid.length هو عدد الصفوف وgrid[0].length هو عدد الأعمدة في الصف الأول. ولا يلزم حتى أن تكون الصفوف بالطول نفسه (يُسمّى ذلك مصفوفة مُسنَّنة)، وإن كانت معظم الشبكات مستطيلة.
فئة الأدوات Arrays
المصفوفات نفسها لا تكاد تملك دوالًّا - فلا يمكنك فرز إحداها أو طباعتها مباشرةً بطريقة مفيدة. توفّر جافا فئة أدوات java.util.Arrays مليئةً بمساعِدات ساكنة لهذا الغرض بالضبط:
بضعة أمور تعتمد عليها هنا. الدالة Arrays.toString(arr) هي الطريقة الصحيحة لطباعة مصفوفة - فطباعة إحداها مباشرةً تعطيك سلسلةً عديمة الفائدة مثل [I@1b6d3586. والدالة Arrays.sort تفرز في المكان. والدالة Arrays.copyOf هي طريقتك في "تغيير الحجم": إذ تُرجع مصفوفة جديدة بالطول الذي تطلبه، مُكمِّلةً بالقيم الافتراضية (الخانتان الإضافيتان أعلاه قيمتهما 0). والدالة Arrays.binarySearch لا تعمل بشكل صحيح إلا على مصفوفة مفروزة مسبقًا.
المصفوفات مقابل ArrayList
استخدم مصفوفة عادية حين يكون الحجم معروفًا مسبقًا ولن يتغير - مجموعة ثابتة من أيام الأسبوع، لوح لعبة، بيانات بكسلات. المصفوفات سريعة وخفيفة، لكن طولها الثابت قيدٌ حقيقي: لا يمكنك إضافة عنصر سابع إلى مصفوفة طولها 6 دون بناء واحدة جديدة بالكامل.
وفي اللحظة التي تحتاج فيها إلى إضافة العناصر وإزالتها بحرية، تصير المصفوفة غير عملية. ولهذا وُجد ArrayList - قائمة قابلة لتغيير الحجم وللنمو، تتولّى عنك عملية النسخ.
التالي: ArrayList
حين لا تعرف كم عنصرًا سيكون لديك، أو حين يتغير ذلك العدد أثناء تشغيل برنامجك، تقف مصفوفة الطول الثابت عائقًا. والإجابة من جافا هي ArrayList: قائمة تنمو وتنكمش حسب الطلب، بدالّتَي add وremove بدلًا من المناورة بالفهارس. وذلك ما سيأتي تاليًا.
الأسئلة الشائعة
كيف تعلن عن مصفوفة وتهيئها في جافا؟
أعلن عن النوع مع أقواس مربعة وأسند له قيمة حرفية: int[] nums = {1, 2, 3};. ولإنشاء مصفوفة فارغة بحجم ثابت، استخدم new: int[] nums = new int[5]; - يمنحك ذلك 5 خانات، كلٌّ منها مملوءة بالقيمة الافتراضية (0 للنوع int، وnull للكائنات).
كيف أحصل على طول مصفوفة في جافا؟
استخدم الحقل length (بدون أقواس): nums.length. إنه حقل وليس دالة، لذا فإن nums.length() خطأ تصريف. لاحظ أن String يستخدم .length() مع الأقواس، بينما تستخدم المصفوفات .length بدونها - وهو خلط شائع.
هل يمكن لمصفوفة جافا أن تغيّر حجمها بعد إنشائها؟
لا. للمصفوفة في جافا طول ثابت يُحدَّد عند إنشائها، ولا يمكنك تكبيره أو تصغيره. إذا احتجت إلى قائمة قابلة لتغيير الحجم، فاستخدم ArrayList بدلًا من ذلك. ولـ"تغيير حجم" مصفوفة، عليك إنشاء مصفوفة جديدة أكبر ونسخ العناصر إليها.
ما هو ArrayIndexOutOfBoundsException في جافا؟
هو خطأ وقت التشغيل الذي تحصل عليه عند الوصول إلى فهرس غير موجود - أقل من 0 أو >= array.length. وعلى خلاف بعض اللغات، لا تُرجع جافا قيمة افتراضية أو null عند الوصول خارج النطاق؛ بل تطلق استثناءً. وتمتد الفهارس الصالحة دائمًا من 0 إلى length - 1.