È possibile avere un HashMap
valore predefinito di ritorno per tutte le chiavi che non si trovano nel set?
getOrDefault()
collegamento
È possibile avere un HashMap
valore predefinito di ritorno per tutte le chiavi che non si trovano nel set?
getOrDefault()
collegamento
Risposte:
[Aggiornare]
Come notato da altre risposte e commentatori, a partire da Java 8 puoi semplicemente chiamare Map#getOrDefault(...)
.
[Originale]
Non esiste un'implementazione di Map che lo fa esattamente, ma sarebbe banale implementare il tuo estendendo HashMap:
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)
a (v == null && !this.containsKey(k))
nel caso in cui abbiano intenzionalmente aggiunto un null
valore. Lo so, questo è solo un caso angolare, ma l'autore potrebbe imbattersi in esso.
!this.containsValue(null)
. Questo è leggermente diverso da !this.containsKey(k)
. La containsValue
soluzione fallirà se ad un'altra chiave è stato assegnato esplicitamente un valore di null
. Ad esempio: map = new HashMap(); map.put(k1, null); V v = map.get(k2);
in questo caso, v
sarà ancora null
corretto?
In Java 8, usa Map.getOrDefault . Prende la chiave e il valore da restituire se non viene trovata alcuna chiave corrispondente.
getOrDefault
è molto bello, ma richiede la definizione predefinita ogni volta che si accede alla mappa. La definizione di una volta un valore predefinito avrebbe anche vantaggi di leggibilità quando si crea una mappa statica di valori.
private static String get(Map map, String s) { return map.getOrDefault(s, "Error"); }
Usa la DefaultedMap di Commons se non hai voglia di reinventare la ruota, ad es.
Map<String, String> map = new DefaultedMap<>("[NO ENTRY FOUND]");
String surname = map.get("Surname");
// surname == "[NO ENTRY FOUND]"
Puoi anche passare a una mappa esistente se non sei incaricato di creare la mappa in primo luogo.
Java 8 ha introdotto un bel metodo predefinito di computeIfAbsent per l' Map
interfaccia che memorizza il valore calcolato in modo pigro e quindi non rompe il contratto della mappa:
Map<Key, Graph> map = new HashMap<>();
map.computeIfAbsent(aKey, key -> createExpensiveGraph(key));
Origine: http://blog.javabien.net/2014/02/20/loadingcache-in-java-8-without-guava/
Disclamer: questa risposta non corrisponde esattamente a ciò che OP ha chiesto, ma può essere utile in alcuni casi abbinare il titolo della domanda quando il numero di chiavi è limitato e la memorizzazione nella cache di valori diversi sarebbe redditizia. Non dovrebbe essere usato nel caso opposto con molte chiavi e lo stesso valore predefinito in quanto ciò sprecherebbe inutilmente memoria.
Non puoi semplicemente creare un metodo statico che fa esattamente questo?
private static <K, V> V getOrDefault(Map<K,V> map, K key, V defaultValue) {
return map.containsKey(key) ? map.get(key) : defaultValue;
}
Puoi semplicemente creare una nuova classe che eredita HashMap e aggiungere il metodo getDefault. Ecco un codice di esempio:
public class DefaultHashMap<K,V> extends HashMap<K,V> {
public V getDefault(K key, V defaultValue) {
if (containsKey(key)) {
return get(key);
}
return defaultValue;
}
}
Penso che non dovresti ignorare il metodo get (tasto K) nella tua implementazione, a causa dei motivi specificati da Ed Staub nel suo commento e perché infrangerai il contratto dell'interfaccia di Map (questo può potenzialmente portare a qualche difficile da trovare insetti).
get
metodo. D'altra parte, la tua soluzione non consente di utilizzare la classe tramite l'interfaccia, il che potrebbe essere il caso.
Uso:
myHashMap.getOrDefault(key, defaultValue);
Lo fa per impostazione predefinita. Ritorna null
.
HashMap
solo per questa funzionalità quando null
va perfettamente bene.
NullObject
modello, o se non vuoi spargere i controlli null nel tuo codice - un desiderio che comprendo completamente.
Ho trovato LazyMap abbastanza utile.
Quando viene chiamato il metodo get (Object) con una chiave che non esiste nella mappa, viene utilizzato il factory per creare l'oggetto. L'oggetto creato verrà aggiunto alla mappa usando la chiave richiesta.
Questo ti permette di fare qualcosa del genere:
Map<String, AtomicInteger> map = LazyMap.lazyMap(new HashMap<>(), ()->new AtomicInteger(0));
map.get(notExistingKey).incrementAndGet();
La chiamata a get
crea un valore predefinito per la chiave specificata. Si specifica come creare il valore predefinito con l'argomento factory su LazyMap.lazyMap(map, factory)
. Nell'esempio sopra, la mappa viene inizializzata su una nuova AtomicInteger
con valore 0.
List<String>
: usando la risposta accettata rischierei di usare lo stesso elenco per ogni nuova chiave, piuttosto che (diciamo) un new ArrayList<String>()
da una fabbrica.
Non direttamente, ma è possibile estendere la classe per modificarne il metodo get. Ecco un esempio pronto per l'uso: 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);
}
Esempio di utilizzo:
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));
Avevo bisogno di leggere i risultati restituiti da un server in JSON dove non potevo garantire che i campi fossero presenti. Sto usando la classe org.json.simple.JSONObject che deriva da HashMap. Ecco alcune funzioni di supporto che ho impiegato:
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; }
}
In progetti misti Java / Kotlin si consideri anche Kotlin's Map.withDefault .