Java Map.get und Map.containsKey

Bei Verwendung der Java-Map-Implementierungen ist es manchmal üblich, die MapMethode get (Object) aufzurufen und unterschiedlich zu reagieren, je nachdem, ob der zurückgegebene Wert null ist oder nicht. Es kann allgemein angenommen werden, dass eine von Map.get (Object) zurückgegebene Null angibt, dass der angegebene Schlüssel in der Karte keinen Eintrag enthält. Dies ist jedoch nicht immer der Fall. Wenn eine Java- MapImplementierung Nullwerte zulässt, kann der MapWert zwar für den angegebenen Schlüssel zurückgegeben werden, dieser Wert kann jedoch Null sein. Oft spielt dies keine Rolle, aber wenn dies der Fall ist, kann mit Map.containsKey () festgestellt werden, ob der MapEintrag einen Schlüsseleintrag enthält. Wenn dies der Fall ist und die MapRückgabe nullbei einem get-Aufruf für denselben Schlüssel erfolgt, ist es wahrscheinlich, dass der Schlüssel einem zugeordnet istnullWert. Mit anderen Worten, das Mapkönnte "true" für zurückgeben, containsKey(Object)während gleichzeitig " null" für "zurückgegeben wird get(Object). Es gibt einige MapImplementierungen, die keine nullWerte zulassen . In diesen Fällen sollte ein nullAufruf von "get" konsistent mit einer Rückgabe von "false" von der Methode "includesKey" übereinstimmen.

In diesem Blogbeitrag zeige ich diese Aspekte von  Map.get(Object)und Map.containsKey(Object). Bevor ich auf diese Demonstration eingehe, möchte ich zunächst darauf hinweisen, dass die Javadoc-Dokumentation für Map.get (Object) ausdrücklich vor den subtilen Unterschieden zwischen Map.get(Object)und warnt Map.containsKey(Object):

Wenn diese Zuordnung Nullwerte zulässt, bedeutet ein Rückgabewert von  null nicht unbedingt, dass die Zuordnung keine Zuordnung für den Schlüssel enthält. Es ist auch möglich, dass die Karte den Schlüssel explizit zuordnet  null. Die  containsKey Operation kann verwendet werden, um diese beiden Fälle zu unterscheiden.

Für die Beispiele des Beitrags werde ich die als nächstes definierte Staatenaufzählung verwenden:

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

Die nächste Codeauflistung verwendet die obige Aufzählung und füllt eine Karte der Bundesstaaten mit ihren Hauptstädten. Die Methode akzeptiert eine Klasse, die die spezifische Implementierung von Map sein soll, die generiert und gefüllt werden soll.

generateStatesMap (Klasse)

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

Die obige Methode kann verwendet werden, um Karten verschiedener Art zu generieren. Der Code wird derzeit nicht angezeigt, aber in meinem Beispiel werden diese Maps mit vier spezifischen Implementierungen erstellt: HashMap, LinkedHashMap, ConcurrentHashMap und EnumMap. Jede dieser vier Implementierungen wird dann durch die Methode ausgeführt demonstrateGetAndContains(Map), die als nächstes gezeigt wird.

demonstrGetAndContains (Karte)

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

Für diese Demonstration habe ich die Karten absichtlich so eingerichtet, dass sie für Montana Nullkapitalwerte haben, für Kansas überhaupt keinen Eintrag. Dies hilft, die Unterschiede in Map.get(Object)und zu demonstrieren Map.containsKey(Object). Da nicht jeder Map-Implementierungstyp Nullwerte zulässt, habe ich den Teil, der Montana ohne Großbuchstaben enthält, in einen Try / Catch-Block eingefügt.

Die Ergebnisse der Ausführung der vier Kartentypen durch den Code werden als Nächstes angezeigt.

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 

Für die drei Map-Typen, für die ich Nullwerte eingeben konnte, gibt der Map.get (Object) -Aufruf null zurück, auch wenn die includesKey (Object) -Methode für Montana "true" zurückgibt, da ich diesen Schlüssel ohne a in die Map eingefügt habe Wert. Für Kansas sind die Ergebnisse konsistent Map.get () gibt null zurück und Map.containsKey () gibt "false" zurück, da in den Karten für Kansas überhaupt kein Eintrag vorhanden ist.

Die obige Ausgabe zeigt auch, dass ich keinen Nullwert für Montanas Kapital in die ConcurrentHashMapImplementierung einfügen konnte (eine NullPointerException wurde ausgelöst).

17. August 2010, 23:23:26 Uhr Dustin.examples.MapContainsGet generateStatesMapSEVERE: java.util.concurrent.ConcurrentHashMap lässt keine Nullwerte zu - java.lang.NullPointerException

Dies hatte den Nebeneffekt der Haltung Map.get(Object)und Map.containsKey(Object)eine einheitlichere jeweiligen null und falsche Rückgabewerte. Mit anderen Worten, es war unmöglich, einen Schlüssel in der Karte zu haben, ohne einen entsprechenden Wert ungleich Null zu haben.

In vielen Fällen wird die Map.get(Object)Arbeit nach Bedarf für die jeweiligen Bedürfnisse verwendet. Beachten Sie jedoch am besten, dass es Unterschiede gibt, Map.get(Object)und Map.containsKey(Object)stellen Sie sicher, dass immer die entsprechende verwendet wird. Es ist auch interessant festzustellen, dass Map auch eine ähnliche containsValue(Object)Methode bietet .

Der Vollständigkeit halber liste ich hier die gesamte Codeliste für die MapContainsGet-Klasse auf:

MapContainsGet.java