Map שומרת זוגות של מפתח וערך
HashMap (מ-java.util) שומרת שיוכים: כל מפתח ממופה לערך אחד, ומחפשים ערכים לפי המפתח שלהם בזמן כמעט קבוע. חשבו על מילון: המילה היא המפתח, וההגדרה היא הערך.
שני פרמטרי הטיפוס הם <KeyType, ValueType>. כאן המפתחות הם String והערכים הם Integer. כמו עם ArrayList, בדרך כלל מגדירים את המשתנה כממשק Map ויוצרים HashMap.
put, get ודריסה
שני דברים שכדאי להפנים:
- מפתח הוא ייחודי.
putעם מפתח קיים מחליפה את הערך שלו ומחזירה את הישן. getעל מפתח חסר מחזירהnull, לא שגיאה. unboxing אוטומטי של ה-nullהזה ל-intזורקNullPointerException, שזה מקור נפוץ לבאגים.
getOrDefault עוקפת את מלכודת ה-null
במקום לבדוק null בכל פעם, בקשו ערך גיבוי:
זו הדרך הנקייה ביותר לטפל בחיפושים של "אולי קיים", והיא מובילה ישר לדפוס המפורסם ביותר של HashMap.
ספירת מופעים
ספירה של כמה פעמים כל פריט מופיע היא המשימה הקלאסית של HashMap:
הדפוס map.put(key, map.getOrDefault(key, 0) + 1) נקרא כך: "קחו את הספירה הנוכחית (או אפס), הוסיפו אחד, ושמרו אותה בחזרה". מקבילה מסודרת יותר היא counts.merge(word, 1, Integer::sum).
בדיקה והסרה
putIfAbsent(key, value) כותבת רק כשהמפתח חסר: שימושי לאתחול עצל.
מעבר בלולאה על HashMap
הלולאה הנפוצה ביותר עוברת על entrySet() ונותנת לכם כל מפתח וערך יחד:
אם צריך רק את המפתחות או רק את הערכים:
אפשר גם להשתמש ב-forEach עם lambda: ages.forEach((name, age) -> System.out.println(name + ": " + age));.
HashMap לא שומרת על סדר
HashMap לא מבטיחה שום סדר מעבר: הסדר הוא מה שה-hashing מייצר, והוא יכול להשתנות בין הרצות. אם צריך סדר צפוי:
LinkedHashMapשומרת על סדר ההכנסה.TreeMapשומרת את המפתחות ממוינים לפי הסדר הטבעי (או לפיComparatorשתספקו).
שלושתן מממשות את הממשק Map, כך שהחלפה ביניהן היא שינוי של שורה אחת בבנאי.
מפתחות חייבים לתמוך ב-hashing
HashMap מוצאת רשומות על ידי hashing של המפתח, ולכן hashCode() ו-equals() של מפתח חייבים להסכים ביניהם. טיפוסים מובנים כמו String ו-Integer כבר עושים את זה נכון. אם אתם משתמשים במחלקה משלכם כמפתח, דרסו גם את equals וגם את hashCode, אחרת שני אובייקטים ש"שווים" במשמעות שלהם ינחתו בדליים שונים והחיפושים ייכשלו באופן מסתורי.
הבא בתור: HashSet
HashMap עונה על השאלה "איזה ערך שמור תחת המפתח הזה?". כשרק חשוב לכם אם משהו קיים בכלל, קבוצה של ערכים ייחודיים בלי נתונים משויכים, הכלי הוא HashSet, שמגיע בעמוד הבא.
שאלות נפוצות
איך יוצרים HashMap ב-Java?
מצהירים עליה עם שני פרמטרי טיפוס, טיפוס המפתח וטיפוס הערך, וקוראים לבנאי: Map<String, Integer> ages = new HashMap<>();. אחר כך מוסיפים רשומות עם ages.put("Ada", 36); וקוראים אותן עם ages.get("Ada");. ייבאו את java.util.HashMap ואת java.util.Map.
איך עוברים בלולאה על HashMap ב-Java?
עברו על map.entrySet() עם לולאת for-each כדי לקבל כל מפתח וערך יחד: for (Map.Entry<String, Integer> e : map.entrySet()) { ... }, וקראו את e.getKey() ואת e.getValue(). אפשר גם לעבור על map.keySet() בשביל המפתחות בלבד או על map.values() בשביל הערכים בלבד. שימו לב ש-HashMap לא שומרת על סדר ההכנסה.
מה ההבדל בין get ל-getOrDefault?
get(key) מחזירה את הערך של מפתח, או null אם המפתח לא קיים, מה שעלול להוביל ל-NullPointerException אם משתמשים בתוצאה ישירות. getOrDefault(key, fallback) מחזירה את הערך אם הוא קיים, ואחרת את ערך הגיבוי שהעברתם, כך שנחסכת בדיקת ה-null. היא שימושית במיוחד לספירה: counts.put(c, counts.getOrDefault(c, 0) + 1).