البرمجة

المصفوفات الديناميكية في جافا

مفهوم المصفوفات الديناميكية (ArrayLists) في جافا

تُعد المصفوفات الديناميكية (ArrayLists) من أهم الهياكل البيانية المستخدمة في لغة جافا لتخزين البيانات بشكل مرن ومتغير الحجم، ما يجعلها خياراً مفضلاً عند الحاجة إلى إدارة مجموعة بيانات قابلة للنمو أو التقلص خلال وقت التشغيل. في هذا المقال سنستعرض مفهوم المصفوفات الديناميكية في جافا، ميزاتها، طريقة استخدامها، مميزاتها وعيوبها، بالإضافة إلى كيفية تطبيقها بشكل عملي مفصل.


1. تعريف المصفوفات الديناميكية (ArrayLists)

المصفوفة الديناميكية (ArrayList) هي نوع خاص من المصفوفات التي تتميز بقدرتها على تغيير حجمها أثناء تنفيذ البرنامج، على عكس المصفوفات التقليدية (Arrays) التي لها حجم ثابت يُحدد عند إنشائها ولا يمكن تغييره لاحقاً. تم تقديم هذه المصفوفة ضمن مكتبة جافا القياسية java.util، وتم تصميمها لتوفير مرونة أكبر في التعامل مع المجموعات الكبيرة من العناصر.

في جوهرها، ArrayList هي قائمة مرتبة (Ordered Collection) تدعم التخزين التسلسلي للعناصر، ويمكنك تخزين أي نوع من الكائنات فيها باستخدام الأنواع العامة (Generics) مما يسمح بكتابة شفرة أكثر أماناً ووضوحاً.


2. الفرق بين المصفوفة التقليدية (Array) و ArrayList

الخاصية المصفوفة التقليدية (Array) المصفوفة الديناميكية (ArrayList)
الحجم ثابت عند الإنشاء قابل للتغيير تلقائياً
نوع البيانات يجب تحديد نوع العنصر مسبقاً تدعم الأنواع العامة (Generics)
الأداء أسرع قليلاً بسبب حجم ثابت أبطأ قليلاً بسبب إعادة تخصيص الذاكرة
دعم الوظائف الإضافية لا يوجد دعم مباشر للوظائف مثل الإضافة أو الحذف توفر وظائف مدمجة مثل الإضافة، الحذف، البحث
الدعم في المكتبة مدمج في اللغة جزء من مكتبة java.util

3. كيفية إنشاء واستخدام ArrayList في جافا

3.1 إنشاء ArrayList

يمكن إنشاء كائن من نوع ArrayList بطرق مختلفة، وأشهرها:

java
import java.util.ArrayList; public class Example { public static void main(String[] args) { // إنشاء ArrayList لتخزين عناصر من نوع String ArrayList list = new ArrayList<>(); // أو تحديد حجم مبدئي ArrayList listWithCapacity = new ArrayList<>(20); } }

3.2 إضافة عناصر إلى ArrayList

يتم استخدام دالة add() لإضافة عناصر:

java
list.add("العنصر الأول"); list.add("العنصر الثاني");

3.3 الوصول إلى العناصر

باستخدام الدالة get(int index):

java
String element = list.get(0); // يحصل على العنصر الأول

3.4 تعديل العناصر

يمكن تعديل عنصر معين عبر دالة set(int index, E element):

java
list.set(1, "عنصر معدل");

3.5 حذف العناصر

يمكن حذف عنصر بواسطة دالة remove() التي تدعم حذف بواسطة الفهرس أو القيمة:

java
list.remove(0); // حذف العنصر الأول list.remove("عنصر معدل"); // حذف العنصر بواسطة القيمة

3.6 التحقق من وجود عنصر

باستخدام contains():

java
boolean exists = list.contains("العنصر الأول");

3.7 معرفة حجم القائمة

java
int size = list.size();

3.8 حذف كافة العناصر

java
list.clear();

4. الخصائص التقنية لمصفوفات ArrayList

  • التوسع التلقائي: عند امتلاء المصفوفة الداخلية (التي تستند إليها ArrayList)، تقوم ArrayList بزيادة سعتها تلقائياً عن طريق إنشاء مصفوفة جديدة أكبر (عادةً الضعف تقريباً)، ثم نسخ العناصر القديمة إليها.

  • التخزين الداخلي: تستخدم ArrayList مصفوفة تقليدية داخلية لتخزين العناصر، وهذا ما يجعل عمليات الوصول عشوائيًا سريعة (O(1.

  • التزامن: ArrayList ليست متزامنة (غير Thread-safe) مما يعني أنه يجب التعامل معها بحذر في بيئات التنفيذ المتعددة الخيوط.

  • دعم الأنواع العامة: منذ إصدار Java 5، تدعم ArrayList الأنواع العامة مما يضمن سلامة نوع البيانات أثناء وقت التجميع (compile-time).


5. مميزات استخدام ArrayList

  • مرونة الحجم: يمكنها استيعاب أي عدد من العناصر دون الحاجة إلى تحديد الحجم مسبقاً.

  • سهولة الاستخدام: توفر واجهة برمجية غنية وسهلة للتعامل مع المجموعات.

  • الوصول العشوائي السريع: تمكّن من الوصول إلى أي عنصر بسرعة عالية.

  • دعم الأنواع العامة: يقلل من الأخطاء المتعلقة بالأنواع ويجعل الكود أكثر وضوحاً.

  • تكامل مع مكتبة جافا: تتوافق بسهولة مع مكونات جافا الأخرى.


6. عيوب ومحددات ArrayList

  • أداء الإضافة والحذف في المنتصف: عمليات الإضافة أو الحذف في مواقع وسط القائمة (غير نهاية القائمة) تكون أقل كفاءة لأن العناصر تحتاج إلى إعادة ترتيب وتحريك.

  • إعادة تخصيص الذاكرة: توسعة السعة التلقائية تتطلب نسخ البيانات القديمة إلى مصفوفة جديدة، ما يستهلك وقتاً إضافياً في بعض الحالات.

  • عدم التزامن: في بيئات متعددة الخيوط، قد تحتاج إلى مزامنة يدوية أو استخدام نسخ متزامنة مثل Vector أو CopyOnWriteArrayList.

  • الاستهلاك الكبير للذاكرة: بسبب التخزين الداخلي في مصفوفة أكبر من حجم البيانات أحياناً (للسماح بالنمو).


7. الفرق بين ArrayList و LinkedList

غالباً ما يقارن المطورون بين ArrayList وLinkedList لفهم أيهما أفضل حسب الحاجة:

الخاصية ArrayList LinkedList
الأداء في الوصول سريع (O(1)) أبطأ (O(n))
الأداء في الإضافة سريع عند النهاية (O(1)) سريع في كل المواقع (O(1))
الأداء في الحذف بطيء في الوسط (O(n)) سريع في أي مكان (O(1))
استخدام الذاكرة أكثر استخداماً بسبب المصفوفة أقل استخداماً بسبب الروابط
التكرار تدعم التكرار تدعم التكرار

8. استخدام ArrayList مع الأنواع العامة Generics

تُستخدم الأنواع العامة مع ArrayList لضمان أن تكون جميع العناصر من نوع محدد، مما يمنع أخطاء التحويل ويجعل الكود أكثر أمانًا وقابلية للصيانة.

مثال:

java
ArrayList numbers = new ArrayList<>(); numbers.add(10); numbers.add(20); // numbers.add("string"); // هذا سيسبب خطأ في وقت التجميع

9. تطبيقات عملية متقدمة

9.1 استخدام Iterator

تستخدم طريقة iterator() للتنقل خلال عناصر ArrayList بطريقة آمنة وسلسة:

java
Iterator iterator = list.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); }

9.2 استخدام for-each loop

java
for (String item : list) { System.out.println(item); }

9.3 تحويل ArrayList إلى مصفوفة

java
String[] array = new String[list.size()]; array = list.toArray(array);

9.4 استخدام Stream API مع ArrayList

من جافا 8، يمكن استخدام الـ Streams للقيام بعمليات مثل الفلترة، الترتيب، التكرار:

java
list.stream() .filter(s -> s.startsWith("العنصر")) .forEach(System.out::println);

10. تحليل أداء ArrayList

العملية متوسط الوقت (Big O) الشرح
الوصول إلى عنصر O(1) وصول مباشر بواسطة الفهرس
إضافة عنصر في النهاية O(1) * (عادة) قد تتطلب إعادة تخصيص إذا امتلأت السعة
إضافة عنصر في موقع وسط O(n) تحريك العناصر بعد الموقع
حذف عنصر في موقع وسط O(n) تحريك العناصر لتغطية الفراغ
البحث عن عنصر O(n) فحص كل العناصر حتى العثور على المطلوب

11. الجدول التالي يوضح مقارنة موجزة بين العمليات في ArrayList

العملية ArrayList ملاحظات
إضافة عنصر في النهاية سريع جدًا (عادة) قد يتباطأ في حال إعادة تخصيص السعة
حذف عنصر بطيء إذا كان في الوسط بسبب تحريك باقي العناصر
تعديل عنصر سريع (O(1)) الوصول مباشرة وإعادة التعيين
البحث عن عنصر بطيء (O(n)) يعتمد على حجم القائمة
استخدام الذاكرة أكثر من LinkedList بسبب مصفوفة ذات حجم أكبر

12. نصائح عملية لاستخدام ArrayList بفعالية

  • في حالة الحاجة إلى إدراج أو حذف عناصر كثيراً من مواقع وسط القائمة، من الأفضل استخدام LinkedList.

  • تحديد الحجم المبدئي initial capacity عند إنشاء ArrayList يساعد على تقليل عمليات إعادة التخصيص المكلفة.

  • استخدام الأنواع العامة (Generics) لتجنب أخطاء التحويل وزيادة أمان الكود.

  • التعامل بحذر مع ArrayList في بيئات متعددة الخيوط مع المزامنة المناسبة.

  • استخدام دوال المكتبة المدمجة لتحسين وضوح وسهولة الصيانة.


المصادر

  1. Oracle Java Documentation – ArrayList

    https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html

  2. Java: The Complete Reference, 11th Edition – Herbert Schildt


مصفوفات ArrayList في جافا تشكل ركيزة أساسية لأي مطور يسعى لبناء تطبيقات مرنة وسريعة، حيث تجمع بين سهولة الاستخدام والأداء المعقول، مع توافر العديد من الأدوات البرمجية التي تجعلها قادرة على التعامل مع مجموعات البيانات بطرق متقدمة ومتنوعة.