0Pricing
Java Academy · درس

آلية عمل HashMap

الحاويات والتجزئة والتصادمات

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

ما الذي يخزّنه HashMap

يخزّن HashMap أزواج مفتاح-قيمة، ويوفّر في المتوسط عمليات بحث وإدراج وإزالة بتعقيد O(1).

يحتفظ داخليًا بمصفوفة تُسمى الجدول. وتُسمى كل خانة في هذه المصفوفة حاوية.

  • يحدد المفتاح الحاوية التي يوضع فيها الإدخال.
  • القيمة هي ما تستعيده عند البحث عن المفتاح.
import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        Map<String, Integer> ages = new HashMap<>();
        ages.put("Alice", 30);
        ages.put("Bob", 25);
        System.out.println(ages.get("Alice"));
    }
}

تجزئة المفتاح

عند استدعاء put(key, value)، تستدعي الخريطة key.hashCode() للحصول على قيمة int.

ثم يوزّع HashMap هذه البتات باستخدام دالة داخلية، لكي تتوزع حتى رموز التجزئة الضعيفة عبر الحاويات.

  • يُختزل العدد النهائي باستخدام hash & (table.length - 1) للحصول على فهرس الحاوية.
  • يكون طول الجدول دائمًا قوةً للعدد 2، لذلك يعمل القناع.
public class Main {
    public static void main(String[] args) {
        String key = "Alice";
        int h = key.hashCode();
        int spread = h ^ (h >>> 16);
        int index = spread & (16 - 1);
        System.out.println("hashCode: " + h);
        System.out.println("bucket index: " + index);
    }
}

الحاويات أثناء العمل

يمكن لكل حاوية أن تحتوي على أكثر من إدخال واحد. وعندما يُعيّن مفتاحان إلى الحاوية نفسها، يُسمى ذلك تصادمًا.

التصادمات طبيعية ومتوقعة. ويتعامل HashMap معها بربط الإدخالات معًا في الحاوية.

import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        Map<Integer, String> m = new HashMap<>();
        for (int i = 0; i < 5; i++) {
            m.put(i, "v" + i);
        }
        System.out.println(m.size() + " entries stored");
    }
}

التصادمات والربط المتسلسل

قبل Java 8، كانت جميع الإدخالات المتصادمة موجودة في قائمة مرتبطة أحاديًا داخل الحاوية.

يمر البحث عبر القائمة، مستدعيًا equals() حتى يعثر على المفتاح المطابق.

  • عدد قليل من التصادمات: يبقى التعقيد فعليًا O(1).
  • تصادمات كثيرة في حاوية واحدة: يتدهور التعقيد مقتربًا من O(n) لتلك الحاوية.
import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        Map<String, Integer> m = new HashMap<>();
        m.put("FB", 1);
        m.put("Ea", 2);
        System.out.println("FB hash: " + "FB".hashCode());
        System.out.println("Ea hash: " + "Ea".hashCode());
        System.out.println(m.get("FB") + ", " + m.get("Ea"));
    }
}

سبب تصادم FB وEa

للسلسلتين "FB" و"Ea" قيمة hashCode() نفسها في Java. وهذا مثال كلاسيكي على التصادم.

حتى مع تطابق رموز التجزئة، تُبقي الخريطة السلسلتين منفصلتين لأن equals() يميّز بينهما داخل الحاوية.

public class Main {
    public static void main(String[] args) {
        System.out.println("FB".hashCode() == "Ea".hashCode());
        System.out.println("FB".equals("Ea"));
    }
}

عامل التحميل

يتحكم عامل التحميل في مدى امتلاء الجدول قبل أن ينمو. والقيمة الافتراضية هي 0.75.

  • إذا كانت السعة 16 وعامل التحميل 0.75، تبدأ إعادة التحجيم عند 12 إدخالًا.
  • يهدر عامل التحميل المنخفض الذاكرة، لكنه يقلل التصادمات.
  • يوفّر عامل التحميل المرتفع الذاكرة، لكنه يزيد التصادمات.
import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        Map<Integer, Integer> m = new HashMap<>(16, 0.75f);
        for (int i = 0; i < 12; i++) m.put(i, i);
        System.out.println("Stored " + m.size() + " entries");
    }
}

إعادة تحجيم الجدول

عندما يتجاوز عدد الإدخالات capacity * loadFactor، يتضاعف حجم الجدول.

تُعاد تجزئة كل إدخالة موجودة في الجدول الجديد الأكبر. وهذه عملية مكلفة، لذلك يُعد تحديد الحجم مسبقًا مهمًا للخرائط الكبيرة.

import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        // Pre-size to avoid repeated resizes
        Map<Integer, Integer> m = new HashMap<>(1024);
        for (int i = 0; i < 800; i++) m.put(i, i * 2);
        System.out.println("size = " + m.size());
    }
}

تحديد الحجم مسبقًا لتحسين الأداء

إذا كنت تعرف تقريبًا عدد الإدخالات التي ستخزّنها، فحدّد سعة ابتدائية لتجنب عمليات إعادة التحجيم المتكررة.

قاعدة تقريبية: السعة الابتدائية = expectedSize / 0.75 + 1.

import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        int expected = 1000;
        int capacity = (int) (expected / 0.75) + 1;
        Map<Integer, String> m = new HashMap<>(capacity);
        System.out.println("Initial capacity hint: " + capacity);
        m.put(1, "ok");
        System.out.println(m.get(1));
    }
}

المفاتيح والقيم ذات القيمة null

يسمح HashMap بـ مفتاح واحد قيمته null وعدة قيم قيمتها null.

  • ينتقل المفتاح null دائمًا إلى الحاوية 0، إذ تُعامل تجزئته على أنها 0.
  • استخدم getOrDefault لتجنب الالتباس بين مفتاح مفقود وقيمة null.
import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        Map<String, String> m = new HashMap<>();
        m.put(null, "nullKeyValue");
        m.put("a", null);
        System.out.println(m.get(null));
        System.out.println(m.getOrDefault("missing", "default"));
    }
}

ترتيب التكرار غير مضمون

لا يقدّم HashMap أي ضمان بشأن ترتيب التكرار. ويعتمد الترتيب على رموز التجزئة وتخطيط الحاويات.

إذا كنت تحتاج إلى ترتيب يمكن التنبؤ به، فاستخدم LinkedHashMap لترتيب الإدراج، أو TreeMap للترتيب المصنّف.

import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        Map<String, Integer> m = new HashMap<>();
        m.put("one", 1);
        m.put("two", 2);
        m.put("three", 3);
        for (Map.Entry<String, Integer> e : m.entrySet()) {
            System.out.println(e.getKey() + "=" + e.getValue());
        }
    }
}

تلخيص مسار get()

تتبع عملية البحث الخطوات التالية:

  • احسب hashCode() ووزّع البتات.
  • طبّق قناعًا للعثور على فهرس الحاوية.
  • تنقّل في الحاوية مع مقارنة المفاتيح باستخدام equals().
  • أعِد القيمة المطابقة أو null.

يضمن hashCode الجيد مع equals الصحيح سرعة كل خطوة.

import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        Map<String, Integer> stock = new HashMap<>();
        stock.put("apple", 50);
        stock.put("pear", 20);
        String key = "apple";
        Integer qty = stock.get(key);
        System.out.println(key + " -> " + qty);
    }
}

اختبار سريع

اختبر مدى فهمك لكيفية العثور على HashMap للحاوية.

مراجعة

لقد تعلّمت كيفية عمل HashMap من الداخل:

  • تُجزّأ المفاتيح وتُربط بـ حاويات.
  • تُعالَج التصادمات بربط الإدخالات داخل الحاوية.
  • يؤدي عامل التحميل (0.75) إلى مضاعفة الحجم وإعادة التجزئة.
  • يؤدي تحديد الحجم مسبقًا إلى تجنّب عمليات تغيير الحجم المكلفة، كما أن ترتيب التكرار غير مضمون.

بعد ذلك، سنرى لماذا لا يكفي hashCode وحده من دون equals صحيح.

import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        Map<String, Integer> m = new HashMap<>(64);
        m.put("recap", 1);
        System.out.println("HashMap basics complete: " + m.get("recap"));
    }
}

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

هل درس «آلية عمل HashMap» مجاني؟

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

ماذا ستتعلم في «آلية عمل HashMap»؟

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

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

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

كم من الوقت يستغرق درس «آلية عمل HashMap»؟

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

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

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

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

  1. آلية عمل HashMap
  2. عقد equals وhashCode
  3. تنفيذ hashCode
  4. تحويل الأشجار والأداء
← العودة إلى Java Academy