Menu

Java HashMap: put, get, מעבר בלולאה ודפוסים נפוצים

איך משתמשים ב-HashMap של Java לחיפושי מפתח-ערך: put, get, getOrDefault, containsKey, מעבר על הרשומות והדפוסים שמשתמשים בהם הכי הרבה.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

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).

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל