TreeSet والمجموعات المرتّبة
TreeSet والمجموعات المرتّبة
تمنحك HashSet عمليات تحقق سريعة من العضوية لكنها لا تضمن أي ترتيب. أحيانًا تحتاج إلى التفرّد والترتيب المتوقّع في آنٍ واحد — وهذا بالضبط ما توفّره TreeSet. إدراك متى تلجأ إلى TreeSet بدلًا من HashSet خطوةٌ مهمة نحو كتابة كود Java معبّر وفعّال.
ما الذي يقبع تحت غطاء TreeSet
TreeSet<E> هي مجموعة من نوع NavigableSet مبنيّة على شجرة أحمر-أسود (red-black tree). كل إدراج أو حذف يكون بتعقيد O(log n) بدلًا من O(1) التقريبي في المجموعات المبنية على جدول تقطيع. المقايضة هي أن التكرار يزور العناصر دومًا بترتيب تصاعدي مرتّب، ويصبح بالإمكان استخدام مجموعة غنية من توابع التصفح.
TreeSet الواجهة NavigableSet، التي تمتد من SortedSet، التي تمتد من Set. إن احتجت فقط إلى تكرار مرتّب، اكتب نوع المتغير كـ SortedSet. وإن احتجت إلى توابع التصفح (floor وceiling وغيرها)، استخدم NavigableSet.
الترتيب الطبيعي عبر Comparable
عند إنشاء TreeSet دون تمرير مقارِن، تستخدم الترتيب الطبيعي — الترتيب المعرَّف في تنفيذ Comparable<T> الخاص بالعناصر. جميع أنواع المغلّفات في Java كـInteger وString وLocalDate تُنفّذ Comparable بالفعل، لذا يعمل هذا مباشرة:
بالنسبة لفئاتك الخاصة، تُنفّذ Comparable<T> بتجاوز تابع compareTo. العقد: أعِد رقمًا سالبًا عندما يكون this أصغر من الوسيط، صفرًا عند التساوي، ورقمًا موجبًا عند الكبر.
equals() أيضًا — وإلا ستعاملهما TreeSet كمكرّر بينما لن تفعل HashSet، مما يُنتج أخطاءً محيّرة عند تبديل التنفيذات.
عمليات NavigableSet
تكشف TreeSet توابع تصفح قوية لا مقابل لها في HashSet:
first()/last()— أصغر / أكبر عنصر.floor(e)— أكبر عنصر أصغر من أو يساوي e، أوnull.ceiling(e)— أصغر عنصر أكبر من أو يساوي e، أوnull.lower(e)— أكبر عنصر أصغر تمامًا من e.higher(e)— أصغر عنصر أكبر تمامًا من e.headSet(to)/tailSet(from)— عرض للعناصر قبل / من قيمة معيّنة.subSet(from, to)— عرض حيّ للنطاق [from, to).pollFirst()/pollLast()— إزالة وإعادة أصغر / أكبر عنصر.
headSet وtailSet وsubSet عروضًا مدعومة. تنتقل التعديلات عبر العرض إلى المجموعة الأصلية والعكس صحيح — مفيد لعمليات الحذف بالنطاق أو التكرار على نطاق دون نسخ.
الترتيب التنازلي
تريد التكرار من الأكبر إلى الأصغر دون كتابة مقارِن مخصص؟ استدعِ descendingSet():
متى تختار TreeSet بدلًا من HashSet
- تحتاج إلى التكرار على العناصر بترتيب مرتّب.
- تحتاج إلى استعلامات نطاق (جميع العناصر بين X وY).
- تحتاج إلى الحصول على الحد الأدنى أو الأقصى بسرعة.
- عناصرك تُنفّذ
Comparable(أو تزوّدComparator).
إن لم ينطبق شيء من هذا، التزم بـHashSet — عملياتها بتعقيد O(1) تتفوق على O(log n) لدى TreeSet في اختبارات العضوية البحتة على نطاق واسع.
الخلاصة
تخزّن TreeSet عناصر فريدة بترتيب مرتّب باستخدام شجرة أحمر-أسود. يعتمد الترتيب الطبيعي على Comparable؛ زوّد Comparator حين تحتاج ترتيبًا مختلفًا أو حين لا يُنفّذ نوع العنصر Comparable. واجهة برمجة التطبيقات NavigableSet — floor وceiling وheadSet وsubSet وغيرها — تجعل عمليات البحث بالنطاق معبّرة وفعّالة. في الدرس التالي سنتناول المكافئ الخرائطي لـTreeSet: وهما TreeMap وLinkedHashMap.