ترتيب Map حسب القيم في Java: كيف لا تقع في الأخطاء القديمة (والجديدة أيضًا)
\n\n«أحتاج ترتيب HashMap حسب القيم!» — هذا السؤال على Stack Overflow يجمع آلاف المشاهدات وعشرات الإجابات. يبدو الأمر بسيطًا؟ تأخذ Map، ترتبها حسب values()… وتصاب بالصداع. لأن ترتيب Map حسب القيم في Java هي المهمة التي يرتكب فيها المبتدئون (وحتى المطورون ذوو الخبرة) نفس الأخطاء مرارًا وتكرارًا.
في هذه المقالة، سنحلل 5 من أكثر الأخطاء شيوعًا عند ترتيب HashMap حسب القيم، ونعرض الحلول الصحيحة ونقدم قوالب كود جاهزة. هيا بنا!
\n\nالخطأ رقم 1: محاولة ساذجة لترتيب HashMap نفسه
\n\nالخطأ الأكثر شيوعًا هو الاعتقاد أنه يمكن ترتيب HashMap «في مكانه». HashMap لا يضمن ترتيب العناصر — هذه خاصية أساسية له. لا يمكنك ببساطة استدعاء sort() على HashMap لأنه لا يحتوي على هذه الطريقة.
إليك كيف لا يجب القيام بذلك:
\n\n// ❌ خطأ: لا يمكن ترتيب HashMap مباشرة\nMap<String, Integer> scores = new HashMap<>();\nscores.put("Alice", 95);\nscores.put("Bob", 80);\nscores.put("Charlie", 90);\n\n// هذه الطريقة غير موجودة!\n// scores.sort(...); // خطأ في الترجمة\n\nلماذا لا يعمل هذا؟ يستخدم HashMap التجزئة لتخزين أزواج المفتاح-القيمة. يتم تحديد الترتيب بواسطة رموز التجزئة للمفاتيح ويمكن أن يتغير عند إضافة عناصر جديدة. ترتيب Map حسب القيم ممكن فقط من خلال إنشاء هيكل بيانات جديد — مثل LinkedHashMap.
الحل الصحيح: إنشاء LinkedHashMap جديد
\n\n// ✅ صحيح: ترتيب Entry وحفظه في LinkedHashMap\nMap<String, Integer> sortedMap = scores.entrySet()\n .stream()\n .sorted(Map.Entry.comparingByValue())\n .collect(Collectors.toMap(\n Map.Entry::getKey,\n Map.Entry::getValue,\n (e1, e2) -> e1, // حل التعارضات (إذا تكررت المفاتيح)\n LinkedHashMap::new // الحفاظ على ترتيب الإدراج\n ));\n\nSystem.out.println(sortedMap);\n// {Bob=80, Charlie=90, Alice=95} — مرتبة تصاعديًا!\n\nالخطأ رقم 2: نسيان LinkedHashMap عند التجميع
\n\nحتى مطورو Java ذوو الخبرة يكتبون أحيانًا هذا الكود:
\n\n// ⚠️ صحيح تقريبًا، لكن هناك فارق\nMap<String, Integer> result = scores.entrySet()\n .stream()\n .sorted(Map.Entry.comparingByValue())\n .collect(Collectors.toMap(\n Map.Entry::getKey,\n Map.Entry::getValue\n )); // افتراضيًا، Collectors.toMap() ينشئ HashMap!\n\nما المشكلة؟ Collectors.toMap() بدون تحديد المزود (supplier) ينشئ HashMap. و HashMap لا يضمن الترتيب! سيتم تنفيذ الترتيب، لكن النتيجة قد تبدو غير مرتبة. هذا واحد من أكثر أخطاء Java عند ترتيب Map خداعًا.
كيفية الإصلاح
\n\nحدد دائمًا LinkedHashMap::new كوسيط رابع:
// ✅ مضمون الصحة\nMap<String, Integer> result = scores.entrySet()\n .stream()\n .sorted(Map.Entry.comparingByValue())\n .collect(Collectors.toMap(\n Map.Entry::getKey,\n Map.Entry::getValue,\n (oldVal, newVal) -> oldVal,\n LinkedHashMap::new // النقطة الأساسية!\n ));\n\nالخطأ رقم 3: الترتيب حسب القيم عندما لا تكون قابلة للمقارنة (Comparable)
\n\nلنفترض أن لديك Map<String, CustomObject>. تكتب:
// ❌ خطأ: CustomObject لا يطبق Comparable\nMap<String, CustomObject> map = new HashMap<>();\n// ...\nmap.entrySet().stream()\n .sorted(Map.Entry.comparingByValue()) // خطأ في الترجمة!\n