Java Map.get и Map.containsKey

Когато се използват реализации на Java на Map, понякога е обичайно да се извиква Mapметодът 'get (Object) и да се реагира по различен начин въз основа на това дали върнатата стойност е нула или не. Може да се направи често срещано предположение, че нула, върната от Map.get (Object), показва, че в картата няма запис с предоставения ключ, но това не винаги е така. Всъщност, ако Mapизпълнението на Java позволява нулеви стойности, тогава е възможно за Mapда върне стойността си за дадения ключ, но тази стойност може да е нула. Често това няма значение, но ако има, човек може да използва Map.containsKey (), за да определи дали Mapзаписът има ключов запис. Ако това се случи, и Mapвръща nullпри извикване за получаване за същия ключ, вероятно е ключът да се преобразува в anullстойност. С други думи, това Mapможе да върне „true“ за, containsKey(Object)докато в същото време се връща „ null“ за get(Object). Има някои Mapреализации, които не позволяват nullстойности. В тези случаи nullизвикването от "get" трябва последователно да съответства на "false" връщане от метода "containsKey".

В тази публикация в блога демонстрирам тези аспекти на  Map.get(Object)и Map.containsKey(Object). Преди да вляза в тази демонстрация, първо ще посоча, че документацията на Javadoc за Map.get (Object) изрично предупреждава за фините разлики между Map.get(Object)и Map.containsKey(Object):

Ако тази карта позволява нулеви стойности, тогава връщаната стойност на  null не означава непременно, че картата не съдържа картографиране за ключа; възможно е и картата изрично да картографира ключа  null. В  containsKey операцията може да се използва за разграничаване на тези два случая.

За примерите на публикацията ще използвам State enum, дефиниран по-нататък:

Държави.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; } } 

Следващият списък с кодове използва изброеното по-горе и попълва карта на щатите до техните столици. Методът приема клас, който трябва да бъде специфичната реализация на Map, която трябва да бъде генерирана и попълнена.

generatedStatesMap (клас)

/** * 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), който е показан по-нататък.

demonstrateGetAndContains (Карта)

/** * 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()); } 

За тази демонстрация умишлено настроих Maps да имат нулеви капиталови стойности за Монтана, за да няма въвеждане за Канзас. Това помага да се демонстрират разликите в Map.get(Object)и Map.containsKey(Object). Тъй като не всеки тип на изпълнение на Map позволява нулеви стойности, заобиколих частта, която поставя Монтана без главни букви в блока try / catch.

Резултатите от пускането на четирите типа Карти през кода се появяват по-нататък.

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 

За трите типа карти, за които успях да въведа нулеви стойности, извикването Map.get (Object) връща null, дори когато методът containsKey (Object) връща „true“ за Монтана, защото аз поставих този ключ в картата без стойност. За Канзас резултатите са последователно Map.get () връща null и Map.containsKey () връща "false", тъй като няма никакъв запис в Maps for Kansas.

Резултатът по-горе също демонстрира, че не можах да вложа нулева стойност за капитала на Монтана в ConcurrentHashMapизпълнението (беше хвърлено NullPointerException).

17 август 2010 г. 23:23:26 прах.

Това имаше страничен ефект от запазването Map.get(Object)и Map.containsKey(Object)по-последователни съответни нулеви и фалшиви връщани стойности. С други думи, беше невъзможно ключът да бъде в картата, без да има съответна ненулева стойност.

В много случаи се използват Map.get(Object)произведения според нуждите за конкретните нужди, но най-добре е да се помни, че има разлики между тях Map.get(Object)и Map.containsKey(Object)да се гарантира, че винаги се използва подходящото. Също така е интересно да се отбележи, че Map също има подобен containsValue(Object)метод.

Тук изброявам целия списък с кодове за класа MapContainsGet за пълнота:

MapContainsGet.java