Est-il possible d'avoir un HashMap
retour d'une valeur par défaut pour toutes les clés qui ne sont pas trouvées dans l'ensemble?
Est-il possible d'avoir un HashMap
retour d'une valeur par défaut pour toutes les clés qui ne sont pas trouvées dans l'ensemble?
Réponses:
[Mettre à jour]
Comme indiqué par d'autres réponses et commentateurs, à partir de Java 8, vous pouvez simplement appeler Map#getOrDefault(...)
.
[Original]
Il n'y a pas d'implémentation de Map qui fait exactement cela, mais il serait trivial d'implémenter la vôtre en étendant 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)
à (v == null && !this.containsKey(k))
au cas où ils ajoutaient volontairement une null
valeur. Je sais, ce n'est qu'un cas secondaire, mais l'auteur peut y tomber.
!this.containsValue(null)
. C'est subtilement différent de !this.containsKey(k)
. La containsValue
solution échouera si une autre clé a été explicitement affectée à la valeur null
. Par exemple: map = new HashMap(); map.put(k1, null); V v = map.get(k2);
dans ce cas, v
sera toujours null
, correct?
Dans Java 8, utilisez Map.getOrDefault . Il prend la clé et la valeur à renvoyer si aucune clé correspondante n'est trouvée.
getOrDefault
est très agréable, mais nécessite la définition par défaut à chaque fois que la carte est accédée. La définition unique d'une valeur par défaut aurait également des avantages de lisibilité lors de la création d'une carte statique de valeurs.
private static String get(Map map, String s) { return map.getOrDefault(s, "Error"); }
Utilisez DefaultedMap de Commons si vous n'avez pas envie de réinventer la roue, par exemple,
Map<String, String> map = new DefaultedMap<>("[NO ENTRY FOUND]");
String surname = map.get("Surname");
// surname == "[NO ENTRY FOUND]"
Vous pouvez également transmettre une carte existante si vous n'êtes pas en charge de la création de la carte en premier lieu.
Java 8 a introduit une belle méthode par défaut computeIfAbsent pour l' Map
interface qui stocke la valeur calculée paresseusement et ne rompt donc pas le contrat de carte:
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: Cette réponse ne correspond pas exactement à ce que OP a demandé, mais peut être pratique dans certains cas, correspondre au titre de la question lorsque le nombre de clés est limité et que la mise en cache de différentes valeurs serait rentable. Il ne devrait pas être utilisé dans le cas contraire avec beaucoup de clés et la même valeur par défaut car cela gaspillerait inutilement de la mémoire.
Ne pouvez-vous pas simplement créer une méthode statique qui fait exactement cela?
private static <K, V> V getOrDefault(Map<K,V> map, K key, V defaultValue) {
return map.containsKey(key) ? map.get(key) : defaultValue;
}
Vous pouvez simplement créer une nouvelle classe qui hérite de HashMap et ajouter la méthode getDefault. Voici un exemple de code:
public class DefaultHashMap<K,V> extends HashMap<K,V> {
public V getDefault(K key, V defaultValue) {
if (containsKey(key)) {
return get(key);
}
return defaultValue;
}
}
Je pense que vous ne devriez pas surcharger la méthode get (K key) dans votre implémentation, à cause des raisons spécifiées par Ed Staub dans son commentaire et parce que vous allez rompre le contrat de l'interface Map (cela peut potentiellement conduire à des problèmes difficiles à trouver Bugs).
get
méthode. D'autre part, votre solution ne permet pas d'utiliser la classe via l'interface, ce qui peut souvent être le cas.
Utilisation:
myHashMap.getOrDefault(key, defaultValue);
Il le fait par défaut. Il revient null
.
HashMap
- classer juste pour cette fonctionnalité quand null
c'est parfaitement bien.
NullObject
modèle, cependant, ou si vous ne voulez pas disperser des vérifications nulles dans votre code - un désir que je comprends parfaitement.
J'ai trouvé la LazyMap très utile.
Lorsque la méthode get (Object) est appelée avec une clé qui n'existe pas dans la carte, la fabrique est utilisée pour créer l'objet. L'objet créé sera ajouté à la carte à l'aide de la clé demandée.
Cela vous permet de faire quelque chose comme ceci:
Map<String, AtomicInteger> map = LazyMap.lazyMap(new HashMap<>(), ()->new AtomicInteger(0));
map.get(notExistingKey).incrementAndGet();
L'appel à get
crée une valeur par défaut pour la clé donnée. Vous spécifiez comment créer la valeur par défaut avec l'argument de fabrique à LazyMap.lazyMap(map, factory)
. Dans l'exemple ci-dessus, la carte est initialisée à un nouveau AtomicInteger
avec la valeur 0.
List<String>
- en utilisant la réponse acceptée, je risquerais d'utiliser la même liste pour chaque nouvelle clé, plutôt que (disons) une new ArrayList<String>()
d'une usine.
Pas directement, mais vous pouvez étendre la classe pour modifier sa méthode get. Voici un exemple prêt à l'emploi: 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);
}
Exemple d'utilisation:
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));
J'avais besoin de lire les résultats renvoyés par un serveur en JSON où je ne pouvais pas garantir que les champs seraient présents. J'utilise la classe org.json.simple.JSONObject qui est dérivée de HashMap. Voici quelques fonctions d'aide que j'ai employées:
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; }
}
Dans les projets mixtes Java / Kotlin, considérez également Map.withDefault de Kotlin .