web-dev-qa-db-fra.com

HashMap pour retourner la valeur par défaut pour les clés non trouvées?

Est-il possible qu’une variable HashMap renvoie une valeur par défaut pour toutes les clés non trouvées dans l’ensemble?

114
Larry

[Mettre à jour]

Comme indiqué par d’autres réponses et commentateurs, à partir de Java 8, vous pouvez simplement appeler Map#getOrDefault(...) .

[Original]

Aucune implémentation de Map ne le fait exactement, 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;
  }
}
94
maerics

En Java 8, utilisez Map.getOrDefault . Il faut la clé et la valeur à renvoyer si aucune clé correspondante n'est trouvée.

144
Spycho

Utilisez DefaultedMap de Commons si vous ne souhaitez pas 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 chargé de la créer.

69
Dave Newton

Java 8 a introduit une méthode Nice computeIfAbsent default à une interface Map qui stocke la valeur calculée paresseux 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 l'OP a demandé, mais peut être utile dans certains cas, mettre le titre de la question en correspondance lorsque le nombre de clés est limité et que la mise en cache de valeurs différentes 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.

38
Vadzim

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;
}
12
Shervin Asgari

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 (clé K) dans votre implémentation, à cause des raisons spécifiées par Ed Staub dans son commentaire et parce que vous allez casser l'interface contract of Map trouver des bugs). 

10
Ivan Mushketyk

Utilisation:

myHashMap.getOrDefault(key, defaultValue);
5
Diego Alejandro

Il le fait par défaut. Il retourne null.

3
mrkhrts

J'ai trouvé le 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 en utilisant 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 indiquez comment créer la valeur par défaut avec l'argument factory à LazyMap.lazyMap(map, factory). Dans l'exemple ci-dessus, la carte est initialisée à une nouvelle AtomicInteger avec la valeur 0.

1
Benedikt Köppel

Sur Java 8+

Map.getOrDefault(Object key,V defaultValue)
1
Eduardo

Je devais lire les résultats renvoyés par un serveur 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'assistance que j'ai utilisé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; }
}   
0
BuvinJ

Dans les projets mixtes Java/Kotlin, prenez également en compte Map.withDefault de Kotlin.

0
Vadzim

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/ExtendedVersionofjavautilHashMapthatprovidesanextendedmethodaccpetingadefaultvalue.htm

0
piokuc
/**
 * 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));
0
KomodoDave