Java Map.get ו- Map.containsKey

בעת שימוש במישומי Map של Java, לעיתים מקובל להפעיל את Mapשיטת ה- get (Object) ולהגיב באופן שונה על סמך הערך שהוחזר הוא ריק או לא. ניתן להניח הנחה נפוצה ש- null שהוחזר מ- Map.get (Object) מציין שאין ערך עם המפתח המסופק במפה, אך זה לא תמיד המקרה. אכן, אם Mapיישום Java מאפשר ערכי null, אז זה יכול Mapלהחזיר את הערך שלו עבור המפתח הנתון, אך ערך זה עשוי להיות null. לעתים קרובות זה לא משנה, אבל אם כן, אפשר להשתמש ב- Map.containsKey () כדי לקבוע אם Mapלערך יש ערך מפתח. אם זה עושה Mapוהחזרת nullהשיחה לקבל עבור אותו מפתח זה, סביר להניח שהמפתח ממפה ל- anullערך. במילים אחרות, זה Mapעשוי להחזיר "אמיתי" containsKey(Object)ובו בזמן לחזור " null" עבור get(Object). יש כמה Mapיישומים שאינם מאפשרים nullערכים. במקרים אלה, nullמשיחת "קבל" צריך להתאים באופן עקבי לתשואה "כוזבת" משיטת "containKey".

בפוסט בבלוג הזה אני מדגים היבטים אלה של  Map.get(Object)ו- Map.containsKey(Object). לפני שנכנס ההפגנה כי, אקדים לציין כי תיעוד Javadoc עבור Map.get (Object) אין להזהיר במפורש על ההבדלים הדקים בין Map.get(Object)לבין Map.containsKey(Object):

אם מפה זו מאפשרת ערכי null, ערך החזרה של  null לא בהכרח מעיד על כך שהמפה אינה מכילה מיפוי עבור המפתח; ייתכן גם שהמפה ממפה במפורש את המפתח אליו  nullcontainsKey הפעולה עשויה לשמש להבחין בין שני המקרים הללו.

לדוגמאות הפוסט, אשתמש באומנות המדינות המוגדרת בהמשך:

States.java

package dustin.examples; /** * Enum representing select western states in the United Sates. */ public enum States { ARIZONA("Arizona"), CALIFORNIA("California"), COLORADO("Colorado"), IDAHO("Idaho"), KANSAS("Kansas"), MONTANA("Montana"), NEVADA("Nevada"), NEW_MEXICO("New Mexico"), NORTH_DAKOTA("North Dakota"), OREGON("Oregon"), SOUTH_DAKOTA("South Dakota"), UTAH("Utah"), WASHINGTON("Washington"), WYOMING("Wyoming"); /** State name. */ private String stateName; /** * Parameterized enum constructor accepting a state name. * * @param newStateName Name of the state. */ States(final String newStateName) { this.stateName = newStateName; } /** * Provide the name of the state. * * @return Name of the state */ public String getStateName() { return this.stateName; } } 

רישום הקוד הבא משתמש באלום שלמעלה ומאכלס מפת מדינות לערי בירתם. השיטה מקבלת מחלקה שאמורה להיות היישום הספציפי של מפה שייווצר ותאוכלס.

createStatesMap (Class)

/** * Generate and populate a Map of states to capitals with provided Map type. * This method also logs any Map implementations for which null values are * not allowed. * * @param mapClass Type of Map to be generated. * @return Map of states to capitals. */ private static Map generateStatesMap(Class mapClass) { Map mapToPopulate = null; if (Map.class.isAssignableFrom(mapClass)) { try { mapToPopulate = mapClass != EnumMap.class ? (Map) mapClass.newInstance() : getEnumMap(); mapToPopulate.put(States.ARIZONA, "Phoenix"); mapToPopulate.put(States.CALIFORNIA, "Sacramento"); mapToPopulate.put(States.COLORADO, "Denver"); mapToPopulate.put(States.IDAHO, "Boise"); mapToPopulate.put(States.NEVADA, "Carson City"); mapToPopulate.put(States.NEW_MEXICO, "Sante Fe"); mapToPopulate.put(States.NORTH_DAKOTA, "Bismark"); mapToPopulate.put(States.OREGON, "Salem"); mapToPopulate.put(States.SOUTH_DAKOTA, "Pierre"); mapToPopulate.put(States.UTAH, "Salt Lake City"); mapToPopulate.put(States.WASHINGTON, "Olympia"); mapToPopulate.put(States.WYOMING, "Cheyenne"); try { mapToPopulate.put(States.MONTANA, null); } catch (NullPointerException npe) { LOGGER.severe( mapToPopulate.getClass().getCanonicalName() + " does not allow for null values - " + npe.toString()); } } catch (InstantiationException instantiationException) { LOGGER.log( Level.SEVERE, "Unable to instantiate Map of type " + mapClass.getName() + instantiationException.toString(), instantiationException); } catch (IllegalAccessException illegalAccessException) { LOGGER.log( Level.SEVERE, "Unable to access Map of type " + mapClass.getName() + illegalAccessException.toString(), illegalAccessException); } } else { LOGGER.warning("Provided data type " + mapClass.getName() + " is not a Map."); } return mapToPopulate; } 

ניתן להשתמש בשיטה לעיל להפקת מפות מסוגים שונים. אני לא מציג את הקוד כרגע, אבל הדוגמה שלי יוצרת מפות אלה עם ארבע יישומים ספציפיים: HashMap, LinkedHashMap, ConcurrentHashMap ו- EnumMap. כל אחת מארבע היישומים הללו מופעלת באמצעות השיטה demonstrateGetAndContains(Map)שתוצג בהמשך.

demonstrGetAndContains (מפה)

/** * Demonstrate Map.get(States) and Map.containsKey(States). * * @param map Map upon which demonstration should be conducted. */ private static void demonstrateGetAndContains(final Map map) { final StringBuilder demoResults = new StringBuilder(); final String mapType = map.getClass().getCanonicalName(); final States montana = States.MONTANA; demoResults.append(NEW_LINE); demoResults.append( "Map of type " + mapType + " returns " + (map.get(montana)) + " for Map.get() using " + montana.getStateName()); demoResults.append(NEW_LINE); demoResults.append( "Map of type " + mapType + " returns " + (map.containsKey(montana)) + " for Map.containsKey() using " + montana.getStateName()); demoResults.append(NEW_LINE); final States kansas = States.KANSAS; demoResults.append( "Map of type " + mapType + " returns " + (map.get(kansas)) + " for Map.get() using " + kansas.getStateName()); demoResults.append(NEW_LINE); demoResults.append( "Map of type " + mapType + " returns " + (map.containsKey(kansas)) + " for Map.containsKey() using " + kansas.getStateName()); demoResults.append(NEW_LINE); LOGGER.info(demoResults.toString()); } 

לצורך הדגמה זו, הגדרתי בכוונה את המפות כך שיהיו ערכי הון אפסים עבור מונטנה שלא תהיה להם כלל כניסה לקנזס. זה עוזר להדגים את ההבדלים ב- Map.get(Object)ו- Map.containsKey(Object). מכיוון שלא כל סוג יישום Map מאפשר ערכי null, הקפתי את החלק שמציב את מונטנה ללא הון בתוך בלוק ניסיון / תפיסה.

התוצאות של הפעלת ארבעת סוגי המפות באמצעות הקוד מופיעות בהמשך.

Aug 17, 2010 11:23:26 PM dustin.examples.MapContainsGet logMapInfo INFO: HashMap: {MONTANA=null, WASHINGTON=Olympia, ARIZONA=Phoenix, CALIFORNIA=Sacramento, WYOMING=Cheyenne, SOUTH_DAKOTA=Pierre, COLORADO=Denver, NEW_MEXICO=Sante Fe, NORTH_DAKOTA=Bismark, NEVADA=Carson City, OREGON=Salem, UTAH=Salt Lake City, IDAHO=Boise} Aug 17, 2010 11:23:26 PM dustin.examples.MapContainsGet demonstrateGetAndContains INFO: Map of type java.util.HashMap returns null for Map.get() using Montana Map of type java.util.HashMap returns true for Map.containsKey() using Montana Map of type java.util.HashMap returns null for Map.get() using Kansas Map of type java.util.HashMap returns false for Map.containsKey() using Kansas Aug 17, 2010 11:23:26 PM dustin.examples.MapContainsGet logMapInfo INFO: LinkedHashMap: {ARIZONA=Phoenix, CALIFORNIA=Sacramento, COLORADO=Denver, IDAHO=Boise, NEVADA=Carson City, NEW_MEXICO=Sante Fe, NORTH_DAKOTA=Bismark, OREGON=Salem, SOUTH_DAKOTA=Pierre, UTAH=Salt Lake City, WASHINGTON=Olympia, WYOMING=Cheyenne, MONTANA=null} Aug 17, 2010 11:23:26 PM dustin.examples.MapContainsGet demonstrateGetAndContains INFO: Map of type java.util.LinkedHashMap returns null for Map.get() using Montana Map of type java.util.LinkedHashMap returns true for Map.containsKey() using Montana Map of type java.util.LinkedHashMap returns null for Map.get() using Kansas Map of type java.util.LinkedHashMap returns false for Map.containsKey() using Kansas Aug 17, 2010 11:23:26 PM dustin.examples.MapContainsGet generateStatesMap SEVERE: java.util.concurrent.ConcurrentHashMap does not allow for null values - java.lang.NullPointerException Aug 17, 2010 11:23:26 PM dustin.examples.MapContainsGet logMapInfo INFO: ConcurrentHashMap: {SOUTH_DAKOTA=Pierre, ARIZONA=Phoenix, WYOMING=Cheyenne, UTAH=Salt Lake City, OREGON=Salem, CALIFORNIA=Sacramento, IDAHO=Boise, NEW_MEXICO=Sante Fe, COLORADO=Denver, NORTH_DAKOTA=Bismark, WASHINGTON=Olympia, NEVADA=Carson City} Aug 17, 2010 11:23:26 PM dustin.examples.MapContainsGet demonstrateGetAndContains INFO: Map of type java.util.concurrent.ConcurrentHashMap returns null for Map.get() using Montana Map of type java.util.concurrent.ConcurrentHashMap returns false for Map.containsKey() using Montana Map of type java.util.concurrent.ConcurrentHashMap returns null for Map.get() using Kansas Map of type java.util.concurrent.ConcurrentHashMap returns false for Map.containsKey() using Kansas Aug 17, 2010 11:23:26 PM dustin.examples.MapContainsGet logMapInfo INFO: EnumMap: {ARIZONA=Phoenix, CALIFORNIA=Sacramento, COLORADO=Denver, IDAHO=Boise, MONTANA=null, NEVADA=Carson City, NEW_MEXICO=Sante Fe, NORTH_DAKOTA=Bismark, OREGON=Salem, SOUTH_DAKOTA=Pierre, UTAH=Salt Lake City, WASHINGTON=Olympia, WYOMING=Cheyenne} Aug 17, 2010 11:23:26 PM dustin.examples.MapContainsGet demonstrateGetAndContains INFO: Map of type java.util.EnumMap returns null for Map.get() using Montana Map of type java.util.EnumMap returns true for Map.containsKey() using Montana Map of type java.util.EnumMap returns null for Map.get() using Kansas Map of type java.util.EnumMap returns false for Map.containsKey() using Kansas 

עבור שלושת סוגי המפה שעבורם הצלחתי להזין ערכי null, הקריאה Map.get (Object) מחזירה null גם כאשר השיטה containKey (Object) מחזירה "true" עבור מונטנה מכיוון ששמתי את המפתח במפה ללא ערך. עבור קנזס, התוצאות הן באופן עקבי Map.get () מחזיר null ו- Map.containsKey () מחזיר "שקר" מכיוון שאין שום ערך במפות עבור קנזס.

הפלט שלעיל גם מדגים שלא יכולתי להכניס ערך ריק להון מונטנה ConcurrentHashMapליישום (NullPointerException הושלך).

17 באוגוסט 2010 23:23:26 dustin.examples.MapContainsGet generatesStatesMapSEVERE: java.util.concurrent.ConcurrentHashMap אינו מאפשר ערכי null - java.lang.NullPointerException

זה היה תוצר הלוואי של שמירה Map.get(Object)ו Map.containsKey(Object)בקסר בהתאמה יותר עקב ערכים חוזרים שווא. במילים אחרות, אי אפשר היה שיהיה מפתח במפה בלי שיהיה לו ערך מקביל שאינו אפס.

במקרים רבים, השימוש Map.get(Object)ביצירות כנדרש בהתאם לצרכים הייחודיים בהישג יד, אבל עדיף לזכור כי ישנם הבדלים בין Map.get(Object)לבין Map.containsKey(Object)כדי לוודא את המתאימה תמיד משמש. מעניין לציין כי Map כוללת גם containsValue(Object)שיטה דומה .

אני מפרט כאן את רשימת הקוד המלאה למחלקת MapContainsGet לקבלת השלמות:

MapContainsGet.java