مفهوم المصفوفات الديناميكية (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 بطرق مختلفة، وأشهرها:
javaimport 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() لإضافة عناصر:
javalist.add("العنصر الأول");
list.add("العنصر الثاني");
3.3 الوصول إلى العناصر
باستخدام الدالة get(int index):
javaString element = list.get(0); // يحصل على العنصر الأول
3.4 تعديل العناصر
يمكن تعديل عنصر معين عبر دالة set(int index, E element):
javalist.set(1, "عنصر معدل");
3.5 حذف العناصر
يمكن حذف عنصر بواسطة دالة remove() التي تدعم حذف بواسطة الفهرس أو القيمة:
javalist.remove(0); // حذف العنصر الأول
list.remove("عنصر معدل"); // حذف العنصر بواسطة القيمة
3.6 التحقق من وجود عنصر
باستخدام contains():
javaboolean exists = list.contains("العنصر الأول");
3.7 معرفة حجم القائمة
javaint size = list.size();
3.8 حذف كافة العناصر
javalist.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 لضمان أن تكون جميع العناصر من نوع محدد، مما يمنع أخطاء التحويل ويجعل الكود أكثر أمانًا وقابلية للصيانة.
مثال:
javaArrayList numbers = new ArrayList<>();
numbers.add(10);
numbers.add(20);
// numbers.add("string"); // هذا سيسبب خطأ في وقت التجميع
9. تطبيقات عملية متقدمة
9.1 استخدام Iterator
تستخدم طريقة iterator() للتنقل خلال عناصر ArrayList بطريقة آمنة وسلسة:
javaIterator iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
9.2 استخدام for-each loop
javafor (String item : list) {
System.out.println(item);
}
9.3 تحويل ArrayList إلى مصفوفة
javaString[] array = new String[list.size()];
array = list.toArray(array);
9.4 استخدام Stream API مع ArrayList
من جافا 8، يمكن استخدام الـ Streams للقيام بعمليات مثل الفلترة، الترتيب، التكرار:
javalist.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 في بيئات متعددة الخيوط مع المزامنة المناسبة.
-
استخدام دوال المكتبة المدمجة لتحسين وضوح وسهولة الصيانة.
المصادر
-
Oracle Java Documentation – ArrayList
https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html -
Java: The Complete Reference, 11th Edition – Herbert Schildt
مصفوفات ArrayList في جافا تشكل ركيزة أساسية لأي مطور يسعى لبناء تطبيقات مرنة وسريعة، حيث تجمع بين سهولة الاستخدام والأداء المعقول، مع توافر العديد من الأدوات البرمجية التي تجعلها قادرة على التعامل مع مجموعات البيانات بطرق متقدمة ومتنوعة.

