إطار المجموعات

نظرة عامة على إطار المجموعات

15 دقيقة الدرس 1 من 14

نظرة عامة على إطار المجموعات

قبل أن يظهر إطار مجموعات Java (JCF) في الإصدار الثاني من Java، كان على المطوّرين بناء هياكل البيانات من الصفر أو الاعتماد على الكلاسَين المُقيَّدَين Vector وHashtable. غيّر الإطار كل ذلك: فهو يوفّر بنية موحّدة لتخزين مجموعات الكائنات واسترجاعها ومعالجتها. فهم تصميمه — الواجهات وعلاقاتها واستخداماتها المقصودة — هو أساس كل درس في هذا المسار.

لماذا إطار عمل وليس مجرد كلاسات؟

يرتكز الإطار على الواجهات (interfaces) لا على الكلاسات المباشرة. ينبغي أن تُعلّم متغيراتك ومعاملاتك بالواجهة (List أو Set أو Map) لا بالتنفيذ (ArrayList أو HashSet أو HashMap). هذا يُتيح لك استبدال تنفيذ بآخر بتغيير سطر واحد فقط — وهو مبدأ يُعرف بـالبرمجة نحو الواجهة.

مبدأ التصميم الأساسي: أعلن المتغيرات باستخدام أكثر واجهة عامة تلبّي حاجتك. اكتب List<String> names = new ArrayList<>(); لا ArrayList<String> names = new ArrayList<>();. إذا قرّرت لاحقًا أن LinkedList أنسب، تغيّر سطر واحد فقط.

هرم الواجهات

يبدأ كل شيء بواجهتَين جذريتَين:

  • Iterable<T> — قمة الهرم. أي كلاس يُنفّذ هذه الواجهة يمكن استخدامه في حلقة for-each. تُعلن عن تابع واحد هو iterator().
  • Collection<T> — تمتد من Iterable وتُضيف العمليات الأساسية: add() وremove() وsize() وcontains() وisEmpty() وعمليات مجمّعة كـaddAll() وremoveAll().

ثلاث واجهات فرعية رئيسية تمتد من Collection:

  • List<T> — تسلسل مرتَّب تُسمح فيه التكرارات وتُصل العناصر بفهرس صحيح.
  • Set<T> — مجموعة بلا عناصر مكررة؛ لا تضمن الترتيب (يعتمد على التنفيذ).
  • Queue<T> — مجموعة مصمَّمة لتخزين العناصر قبل معالجتها، عادةً بترتيب FIFO. تمتد منها Deque (طابور ثنائي الطرف).

Map<K, V> لا تمتد من Collection. تخزّن الخريطة أزواج مفتاح-قيمة؛ كل مفتاح فريد. لها هرم واجهات خاص بها (SortedMap وNavigableMap).

نموذج ذهني سريع

فكّر في كل واجهة باعتبارها تُجيب عن سؤال مختلف حول بياناتك:

  • List — "أهتم بالترتيب والموضع. أعطني العنصر الثالث."
  • Set — "أهتم فقط بوجود شيء ما. لا تكرارات."
  • Queue — "عالج العناصر بترتيب محدد (FIFO أو أولوية...)."
  • Map — "ابحث عن قيمة بمفتاحها."

التنفيذات الملموسة بلمحة سريعة

لكل واجهة عدة تنفيذات مضبوطة لمقايضات أداء مختلفة. إليك نظرة أولية (سيُخصَّص لكل منها درسه الخاص):

  • List: ArrayList (وصول عشوائي سريع)، LinkedList (إدراج/حذف سريع عند الأطراف)
  • Set: HashSet (contains بكلفة O(1))، TreeSet (مرتَّب)، LinkedHashSet (ترتيب الإدراج)
  • Queue / Deque: ArrayDeque (مكدس/طابور سريع)، PriorityQueue (قائمة كومة)
  • Map: HashMap (بحث O(1))، TreeMap (مفاتيح مرتّبة)، LinkedHashMap (ترتيب الإدراج)

أول كود بالمجموعات

لنرى الواجهات الأربع الرئيسية في عمل حقيقي في مقطع واحد قابل للتشغيل:

import java.util.*; public class CollectionsOverview { public static void main(String[] args) { // List — مرتَّب، التكرارات مسموح بها List<String> languages = new ArrayList<>(); languages.add("Java"); languages.add("Kotlin"); languages.add("Java"); // التكرار مسموح System.out.println(languages); // [Java, Kotlin, Java] System.out.println(languages.get(1)); // Kotlin — وصول بالفهرس // Set — لا تكرارات Set<String> unique = new HashSet<>(languages); System.out.println(unique); // [Java, Kotlin] — الترتيب غير مضمون // Queue — معالجة FIFO Queue<String> tasks = new ArrayDeque<>(); tasks.offer("compile"); tasks.offer("test"); tasks.offer("deploy"); System.out.println(tasks.poll()); // compile — يُزيل الرأس // Map — بحث بالمفتاح-القيمة Map<String, Integer> scores = new HashMap<>(); scores.put("Alice", 95); scores.put("Bob", 87); System.out.println(scores.get("Alice")); // 95 System.out.println(scores.containsKey("Charlie")); // false } }
استيراد واحد يكفي: import java.util.*; يغطي كل كلاس وواجهة في إطار المجموعات. في بيئة الإنتاج يُفضّل بعض الفرق الاستيرادات الصريحة للوضوح، لكن الاستيراد بالبدل مقبول تمامًا.

عقد Iterable/Collection

لأن List وSet وQueue تُنفّذ جميعها Iterable في نهاية المطاف، يمكنك التكرار عليها جميعًا بحلقة for-each نفسها — دون الحاجة إلى معرفة النوع المحدد:

// يعمل مع أي Collection — List أو Set أو Queue أو غيرها static void printAll(Collection<?> col) { for (Object item : col) { System.out.println(item); } }

هذه هي قوة الهرم: اكتب الكود مرة واحدة مقابل واجهة، وسيعمل مع كل تنفيذ حالي ومستقبلي.

لا تستخدم الأنواع الخام (raw types). كتابة List list = new ArrayList(); بدون جينيريكس يُجمَّل لكنه يُعطّل سلامة النوع ويُولّد تحذيرات تحويل غير آمن. دائمًا أضف معامل النوع: List<String>.

اختيار الواجهة المناسبة

شجرة قرار عملية قبل أن تبدأ بالكود:

  1. هل تحتاج إلى بحث بالمفتاح-القيمة؟ → Map
  2. هل تحتاج إلى ضمان التفرد؟ → Set
  3. هل تحتاج إلى معالجة FIFO أو بالأولوية؟ → Queue / PriorityQueue
  4. هل تحتاج إلى وصول مرتَّب بالفهرس؟ → List

اختيار الواجهة الصحيحة من البداية يمنع الأخطاء ويُوضّح نيّتك للمطوّرين الآخرين الذين يقرؤون كودك.

الخلاصة

يمنحك إطار مجموعات Java بنية واحدة متماسكة مبنية على الواجهات. تجلس Iterable وCollection في الجذر؛ تُحسّنها List وSet وQueue لأنماط وصول مختلفة؛ وتقف Map منفردة كمخزن مفتاح-قيمة. كل كلاس ملموس (ArrayList وHashSet وHashMap…) هو مجرد تنفيذ لإحدى هذه الواجهات. برمِج نحو الواجهة — سيكون كودك أنظف وأكثر مرونة وأسهل في الاختبار. الدرس التالي يتعمق في ArrayList، أكثر تنفيذات List استخدامًا.