0Pricing
Java Academy · درس

الترتيب المخصص في مجموعات الأشجار

مرّر Comparator إلى TreeMap وTreeSet لتعريف ترتيبات فرز خاصة بالمجال

الترتيب المخصص في مجموعات الأشجار درس مجاني في Java Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Java Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Java Academy 4 دروس في المجموع.

لماذا نستخدم ترتيبًا مخصصًا؟

تستخدم المجموعات الشجرية (TreeMap وTreeSet) الترتيب الطبيعي افتراضيًا. عندما لا تملك كائنات المجال ترتيبًا طبيعيًا، أو تحتاج إلى ترتيب مختلف، تمرّر Comparator عند الإنشاء.

Comparator عند إنشاء TreeMap

مرّر مقارنًا لترتيب المفاتيح وفق قاعدة مخصصة، مثل الترتيب الأبجدي العكسي:

import java.util.*;

TreeMap<String, Integer> map = new TreeMap<>(Comparator.reverseOrder());
map.put("banana", 2);
map.put("apple", 1);
map.put("cherry", 3);

map.forEach((k,v) -> System.out.println(k)); // cherry, banana, apple

Comparator عند إنشاء TreeSet

مرّر مقارنًا إلى TreeSet للتحكم في ترتيب الفرز بشكل مستقل عن الترتيب الطبيعي للعناصر:

TreeSet<String> byLength = new TreeSet<>(
    Comparator.comparingInt(String::length)
              .thenComparing(Comparator.naturalOrder())
);
byLength.addAll(List.of("fig","apple","kiwi","date","banana"));

for (String s : byLength) System.out.print(s + " ");
// fig date kiwi apple banana

ترتيب الكائنات حسب حقول متعددة

سلسِل المقارنات للترتيب حسب حقل أساسي، ثم حسب حقل ثانوي لكسر التعادل:

record Employee(String name, String dept, int salary) {}

TreeSet<Employee> sorted = new TreeSet<>(
    Comparator.comparing(Employee::dept)
              .thenComparingInt(Employee::salary).reversed()
              .thenComparing(Employee::name)
);
sorted.add(new Employee("Alice", "Eng", 90_000));
sorted.add(new Employee("Bob",   "Eng", 85_000));
sorted.add(new Employee("Carol", "HR",  70_000));

for (Employee e : sorted) System.out.println(e.dept()+" "+e.name());

الاتساق مع equals

قاعدة مهمة: يحدّد المقارن الذي تستخدمه TreeSet/TreeMap معنى التساوي بالنسبة إلى المجموعة. فإذا كانت نتيجة comparator.compare(a, b) == 0، فسيُعتبر a وb المفتاح نفسه، حتى إذا كانت نتيجة a.equals(b) هي false.

// Comparator ignoring case — "apple" and "APPLE" become the same key!
TreeSet<String> ci = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
ci.add("apple");
ci.add("APPLE"); // not added — same by comparator
System.out.println(ci.size()); // 1

TreeMap غير الحساسة لحالة الأحرف

من الاحتياجات العملية الشائعة إنشاء TreeMap تكون مفاتيحها سلاسل نصية غير حساسة لحالة الأحرف، وهو أمر مفيد لرؤوس HTTP ومفاتيح الإعدادات:

TreeMap<String, String> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
headers.put("Content-Type", "application/json");
headers.put("content-type", "text/html"); // overwrites!

System.out.println(headers.get("CONTENT-TYPE")); // text/html
System.out.println(headers.size()); // 1

مقارن آمن مع null

ترمي مجموعات الأشجار التي تستخدم الترتيب الطبيعي استثناء NPE عند وجود مفاتيح null. استخدم مقارنًا آمنًا مع null للسماح بـ null بوصفه المفتاح الأصغر:

TreeMap<String, Integer> map = new TreeMap<>(
    Comparator.nullsFirst(Comparator.naturalOrder())
);
map.put(null, 0);
map.put("b", 2);
map.put("a", 1);

map.forEach((k,v) -> System.out.println(k + "=" + v));
// null=0, a=1, b=2

مثال على كائن من المجال: ترتيب المنتجات حسب السعر

رتّب المنتجات حسب السعر في TreeSet، مع استخدام الاسم لكسر التعادل والحفاظ على التفرد:

record Product(String name, double price) {}

TreeSet<Product> catalog = new TreeSet<>(
    Comparator.comparingDouble(Product::price)
              .thenComparing(Product::name)
);
catalog.add(new Product("Widget", 9.99));
catalog.add(new Product("Gadget", 24.99));
catalog.add(new Product("Donut",  9.99));

catalog.forEach(p -> System.out.println(p.name() + " $" + p.price()));
// Donut $9.99, Widget $9.99, Gadget $24.99

استرجاع Comparator

استدعِ comparator() على TreeMap/TreeSet لاسترجاع المقارن المخصص. وتعيد القيمة null عند استخدام الترتيب الطبيعي.

TreeMap<String, Integer> map = new TreeMap<>(Comparator.reverseOrder());
System.out.println(map.comparator() != null); // true

TreeMap<String, Integer> natural = new TreeMap<>();
System.out.println(natural.comparator());      // null

تركيب Comparator

يوفّر Comparator في Java أساليب إنشاء قابلة للسلسلة لتركيب المقارنات بطريقة واضحة:

Comparator<String> comp =
    Comparator.comparingInt(String::length)   // by length
              .thenComparing(Comparator.naturalOrder()); // then alphabetically

TreeSet<String> ts = new TreeSet<>(comp);
ts.addAll(List.of("go", "java", "c", "rust", "py"));
ts.forEach(s -> System.out.print(s + " "));
// c go py java rust

متى لا تستخدم Comparator مخصصًا

تجنّب استخدام مقارن غير متسق مع equals لمفاتيح TreeMap أو عناصر TreeSet، إذ يتسبب ذلك في إدخالات "مفقودة" ومربكة. احرص على أن تكون compare(a, b) == 0 مكافئة تمامًا لـ a.equals(b) لضمان السلوك الصحيح في جميع سياقات المجموعات.

تحقق سريع

تستخدم TreeSet مقارنًا يعتمد على String::length فقط. ماذا يحدث عند إضافة كل من "cat" و"dog"؟

مراجعة: الترتيب المخصص

أهم النقاط:

  • مرّر Comparator إلى مُنشئ TreeMap/TreeSet لتحديد ترتيب مخصص
  • تتحكم المساواة التي يحددها Comparator في التفرد داخل مجموعات الأشجار
  • احرص على توافق Comparator مع equals لتجنب النتائج غير المتوقعة
  • استخدم Comparator.nullsFirst/ nullsLast لترتيب آمن مع القيم null
  • اربط المقارنات باستخدام thenComparing للفرز حسب عدة حقول

الأسئلة الشائعة

هل درس «الترتيب المخصص في مجموعات الأشجار» مجاني؟

نعم — نص درس «الترتيب المخصص في مجموعات الأشجار» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Java Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Java Academy 4 دروس في المجموع.

ماذا ستتعلم في «الترتيب المخصص في مجموعات الأشجار»؟

مرّر Comparator إلى TreeMap وTreeSet لتعريف ترتيبات فرز خاصة بالمجال تتمرن على Java Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Java Academy؟

لا تُشترط خبرة سابقة. Java Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.

كم من الوقت يستغرق درس «الترتيب المخصص في مجموعات الأشجار»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Java Academy هذا؟

نعم. كل درس في Java Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. TreeMap: أزواج المفاتيح والقيم المرتبة
  2. الخرائط الفرعية وعروض النطاق
  3. TreeSet وNavigableSet
  4. الترتيب المخصص في مجموعات الأشجار
← العودة إلى Java Academy