Java Map.get i Map.containsKey

Kada se koriste implementacije Javine mape, često je uobičajeno pozivati Mapmetodu get (Object) i reagirati drugačije na temelju toga je li vraćena vrijednost null ili ne. Uobičajena pretpostavka može biti da nula vraćena iz Map.get (Object) ukazuje da na karti nema unosa s navedenim ključem, ali to nije uvijek slučaj. Zapravo, ako Java Mapimplementacija dopušta null vrijednosti, tada je moguće Mapvratiti vrijednost za zadani ključ, ali ta vrijednost može biti null. To često nije važno, ali ako jest, pomoću Map.containsKey () može se utvrditi Mapima li unos ključni unos. Ako se dogodi i Mappovrati nullse poziv za dobivanje istog tog ključa, tada je vjerojatno da se ključ preslikava unullvrijednost. Drugim riječima, to Mapbi moglo vratiti "true" containsKey(Object)dok bi se istovremeno vratilo " null" za get(Object). Postoje neke Mapimplementacije koje ne dopuštaju nullvrijednosti. U tim bi se slučajevima nullpoziv iz poziva "get" trebao dosljedno podudarati s povratom "false" iz metode "containsKey".

U ovom blogu prikazujem ove aspekte  Map.get(Object)i Map.containsKey(Object). Prije nego što krenem u tu demonstraciju, prvo ću istaknuti da Javadoc-ova dokumentacija za Map.get (Object) izričito upozorava na suptilne razlike između Map.get(Object)i Map.containsKey(Object):

Ako ova karta dopušta null vrijednosti, tada povratna vrijednost  null ne znači nužno da karta ne sadrži mapiranje za ključ; Također je moguće da karta izričito preslikava ključ  nullcontainsKey Postupak može se koristiti za razlikovanje ova dva slučaja.

Za primjere posta koristit ću sljedeći popis država:

Države.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; } } 

Sljedeći popis šifri koristi gornji nabrajanje i popunjava kartu država do njihovih glavnih gradova. Metoda prihvaća klasu koja bi trebala biti specifična implementacija Mape koja se generira i popunjava.

generirajStatesMap (klasa)

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

Gornja metoda može se koristiti za generiranje karata različitih vrsta. Trenutno ne prikazujem kôd, ali moj primjer stvara ove Karte s četiri specifične implementacije: HashMap, LinkedHashMap, ConcurrentHashMap i EnumMap. Svaka od ove četiri implementacije se zatim provodi kroz metodu demonstrateGetAndContains(Map), koja je prikazana dalje.

demonstrateGetAndContains (karta)

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

Za ovu demonstraciju namjerno sam postavio Karte tako da imaju nule vrijednosti kapitala za Montanu da uopće ne ulaze u Kansas. To pomaže pokazati razlike u Map.get(Object)i Map.containsKey(Object). Budući da svaka vrsta implementacije Mape ne dopušta null vrijednosti, okružio sam dio koji Montanu stavlja bez velikog slova unutar bloka try / catch.

Rezultati pokretanja četiri vrste Karti kroz kôd pojavit će se sljedeći.

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 

Za tri vrste karte za koje sam uspio unijeti null vrijednosti, poziv Map.get (Object) vraća nulu čak i kada metoda containsKey (Object) vraća "true" za Montanu jer sam taj ključ stavio na kartu bez vrijednost. Za Kansas su rezultati dosljedno Map.get () vraća nulu, a Map.containsKey () vraća "false" jer u Mapama za Kansas uopće nema unosa.

Gore navedeni izlaz također pokazuje da u ConcurrentHashMapimplementaciju nisam mogao staviti nulu vrijednosti glavnog grada Montane (bačen je NullPointerException).

17. kolovoza 2010. 23:23:26 prašina.examples.MapContainsGet generirajStatesMapSEVERE: java.util.concurrent.ConcurrentHashMap ne dopušta null vrijednosti - java.lang.NullPointerException

To je imalo nuspojavu zadržavanja Map.get(Object)i Map.containsKey(Object)dosljednije odgovarajuće vrijednosti null i false. Drugim riječima, bilo je nemoguće imati ključ na mapi bez odgovarajuće ne-null vrijednosti.

U mnogim se slučajevima upotrebljava Map.get(Object)djela po potrebi za određene potrebe, ali najbolje je zapamtiti da postoje razlike između njih Map.get(Object)i Map.containsKey(Object)osigurati da se uvijek koristi odgovarajuće. Zanimljivo je također napomenuti da i Map ima sličnu containsValue(Object)metodu.

Ovdje navodim cjelokupni popis kodova za klasu MapContainsGet radi cjelovitosti:

MapContainsGet.java