Ist es möglich, HashMap
für alle Schlüssel, die nicht im Set enthalten sind, einen Standardwert zurückzugeben?
Ist es möglich, HashMap
für alle Schlüssel, die nicht im Set enthalten sind, einen Standardwert zurückzugeben?
Antworten:
[Aktualisieren]
Wie von anderen Antworten und Kommentatoren angemerkt, können Sie ab Java 8 einfach aufrufen Map#getOrDefault(...)
.
[Original]
Es gibt keine Map-Implementierung, die dies genau tut, aber es wäre trivial, eine eigene zu implementieren, indem Sie HashMap erweitern:
public class DefaultHashMap<K,V> extends HashMap<K,V> {
protected V defaultValue;
public DefaultHashMap(V defaultValue) {
this.defaultValue = defaultValue;
}
@Override
public V get(Object k) {
return containsKey(k) ? super.get(k) : defaultValue;
}
}
(v == null)
bis anpassen , (v == null && !this.containsKey(k))
falls sie absichtlich einen null
Wert hinzufügen . Ich weiß, dies ist nur ein Eckfall, aber der Autor kann darauf stoßen.
!this.containsValue(null)
. Dies unterscheidet sich geringfügig von !this.containsKey(k)
. Die containsValue
Lösung schlägt fehl, wenn einem anderen Schlüssel explizit der Wert "zugewiesen" wurde null
. Zum Beispiel: Wird map = new HashMap(); map.put(k1, null); V v = map.get(k2);
in diesem Fall v
noch null
richtig sein?
Verwenden Sie in Java 8 Map.getOrDefault . Der Schlüssel und der Wert müssen zurückgegeben werden, wenn kein passender Schlüssel gefunden wird.
getOrDefault
ist sehr schön, erfordert aber bei jedem Zugriff auf die Karte die Standarddefinition. Das einmalige Definieren eines Standardwerts hätte auch Lesbarkeitsvorteile beim Erstellen einer statischen Wertekarte.
private static String get(Map map, String s) { return map.getOrDefault(s, "Error"); }
Verwenden Sie die DefaultedMap von Commons, wenn Sie das Rad nicht neu erfinden möchten , z.
Map<String, String> map = new DefaultedMap<>("[NO ENTRY FOUND]");
String surname = map.get("Surname");
// surname == "[NO ENTRY FOUND]"
Sie können auch eine vorhandene Karte übergeben, wenn Sie nicht erst für die Erstellung der Karte verantwortlich sind.
Java 8 hat eine nette computeIfAbsent- Standardmethode für die Map
Schnittstelle eingeführt, die faul berechneten Wert speichert und so den Kartenvertrag nicht bricht:
Map<Key, Graph> map = new HashMap<>();
map.computeIfAbsent(aKey, key -> createExpensiveGraph(key));
Herkunft: http://blog.javabien.net/2014/02/20/loadingcache-in-java-8-without-guava/
Disclamer: Diese Antwort stimmt nicht genau mit dem überein, was OP gefragt hat, kann jedoch in einigen Fällen nützlich sein, wenn der Schlüsseltitel begrenzt ist und das Zwischenspeichern verschiedener Werte rentabel wäre. Es sollte nicht im umgekehrten Fall mit vielen Schlüsseln und demselben Standardwert verwendet werden, da dies unnötig Speicherplatz verschwenden würde.
Können Sie nicht einfach eine statische Methode erstellen, die genau dies tut?
private static <K, V> V getOrDefault(Map<K,V> map, K key, V defaultValue) {
return map.containsKey(key) ? map.get(key) : defaultValue;
}
Sie können einfach eine neue Klasse erstellen, die HashMap erbt, und die Methode getDefault hinzufügen. Hier ist ein Beispielcode:
public class DefaultHashMap<K,V> extends HashMap<K,V> {
public V getDefault(K key, V defaultValue) {
if (containsKey(key)) {
return get(key);
}
return defaultValue;
}
}
Ich denke, dass Sie die Methode get (K key) in Ihrer Implementierung nicht überschreiben sollten, da Ed Staub in seinem Kommentar Gründe angegeben hat und Sie den Vertrag über die Map-Schnittstelle brechen werden (dies kann möglicherweise zu schwer zu findenden Gründen führen Bugs).
get
Methode nicht zu überschreiben . Auf der anderen Seite erlaubt Ihre Lösung nicht, die Klasse über eine Schnittstelle zu verwenden, was häufig der Fall ist.
Verwenden:
myHashMap.getOrDefault(key, defaultValue);
Dies geschieht standardmäßig. Es kehrt zurück null
.
HashMap
nur für diese Funktionalität eine Unterklasse zu bilden, wenn dies null
vollkommen in Ordnung ist.
NullObject
Muster verwenden oder keine Nullprüfungen in Ihrem Code verteilen möchten - ein Wunsch, den ich vollständig verstehe.
Ich fand die LazyMap sehr hilfreich.
Wenn die Methode get (Object) mit einem Schlüssel aufgerufen wird, der in der Map nicht vorhanden ist, wird das Objekt in der Factory erstellt. Das erstellte Objekt wird mit dem angeforderten Schlüssel zur Karte hinzugefügt.
Auf diese Weise können Sie Folgendes tun:
Map<String, AtomicInteger> map = LazyMap.lazyMap(new HashMap<>(), ()->new AtomicInteger(0));
map.get(notExistingKey).incrementAndGet();
Der Aufruf zum get
Erstellen eines Standardwerts für den angegebenen Schlüssel. Sie geben an, wie der Standardwert mit dem Factory-Argument an erstellt werden soll LazyMap.lazyMap(map, factory)
. Im obigen Beispiel wird die Karte AtomicInteger
mit dem Wert 0 auf eine neue Karte initialisiert .
List<String>
: Wenn ich die akzeptierte Antwort verwende, riskiere ich, für jeden neuen Schlüssel dieselbe Liste zu verwenden, anstatt (sagen wir) eine new ArrayList<String>()
aus einer Fabrik.
Nicht direkt, aber Sie können die Klasse erweitern, um ihre get-Methode zu ändern. Hier ist ein gebrauchsfertiges Beispiel: http://www.java2s.com/Code/Java/Collections-Data-Structure/ExtendedVersionofjavautilHashMapthatprovidesanextendedgetmethodaccpetingadefaultvalue.htm
/**
* Extension of TreeMap to provide default value getter/creator.
*
* NOTE: This class performs no null key or value checking.
*
* @author N David Brown
*
* @param <K> Key type
* @param <V> Value type
*/
public abstract class Hash<K, V> extends TreeMap<K, V> {
private static final long serialVersionUID = 1905150272531272505L;
/**
* Same as {@link #get(Object)} but first stores result of
* {@link #create(Object)} under given key if key doesn't exist.
*
* @param k
* @return
*/
public V getOrCreate(final K k) {
V v = get(k);
if (v == null) {
v = create(k);
put(k, v);
}
return v;
}
/**
* Same as {@link #get(Object)} but returns specified default value
* if key doesn't exist. Note that default value isn't automatically
* stored under the given key.
*
* @param k
* @param _default
* @return
*/
public V getDefault(final K k, final V _default) {
V v = get(k);
return v == null ? _default : v;
}
/**
* Creates a default value for the specified key.
*
* @param k
* @return
*/
abstract protected V create(final K k);
}
Anwendungsbeispiel:
protected class HashList extends Hash<String, ArrayList<String>> {
private static final long serialVersionUID = 6658900478219817746L;
@Override
public ArrayList<Short> create(Short key) {
return new ArrayList<Short>();
}
}
final HashList haystack = new HashList();
final String needle = "hide and";
haystack.getOrCreate(needle).add("seek")
System.out.println(haystack.get(needle).get(0));
Ich musste die Ergebnisse lesen, die von einem Server in JSON zurückgegeben wurden, auf dem ich nicht garantieren konnte, dass die Felder vorhanden sind. Ich verwende die Klasse org.json.simple.JSONObject, die von HashMap abgeleitet ist. Hier sind einige Hilfsfunktionen, die ich verwendet habe:
public static String getString( final JSONObject response,
final String key )
{ return getString( response, key, "" ); }
public static String getString( final JSONObject response,
final String key, final String defVal )
{ return response.containsKey( key ) ? (String)response.get( key ) : defVal; }
public static long getLong( final JSONObject response,
final String key )
{ return getLong( response, key, 0 ); }
public static long getLong( final JSONObject response,
final String key, final long defVal )
{ return response.containsKey( key ) ? (long)response.get( key ) : defVal; }
public static float getFloat( final JSONObject response,
final String key )
{ return getFloat( response, key, 0.0f ); }
public static float getFloat( final JSONObject response,
final String key, final float defVal )
{ return response.containsKey( key ) ? (float)response.get( key ) : defVal; }
public static List<JSONObject> getList( final JSONObject response,
final String key )
{ return getList( response, key, new ArrayList<JSONObject>() ); }
public static List<JSONObject> getList( final JSONObject response,
final String key, final List<JSONObject> defVal ) {
try { return response.containsKey( key ) ? (List<JSONObject>) response.get( key ) : defVal; }
catch( ClassCastException e ) { return defVal; }
}
Berücksichtigen Sie in gemischten Java / Kotlin-Projekten auch Kotlins Map.withDefault .