web-dev-qa-db-fra.com

Implémentation Java Map qui renvoie une valeur par défaut au lieu de null

J'ai un Map<String, List<String>> dans mon code, dans lequel j'éviterais les pointeurs nuls potentiels si la méthode #get() de la carte renvoyait une liste vide au lieu de null. Y a-t-il quelque chose comme ça dans l'API Java? Devrais-je simplement prolonger HashMap?

33
jk.

La réponse de @ Jon est un bon moyen de faire ce que vous demandez directement.

Mais, ce qui me frappe, c’est que vous essayez peut-être de mettre en place une "carte multiple"; c'est-à-dire un mappage d'une clé à une collection de valeurs. Si tel est le cas, vous devriez également consulter les classes à cartes multiples des collections Guava ou Apache commons.

Regarder:

23
Stephen C

Grâce aux méthodes default, Java 8 est désormais intégré à Map::getOrDefault :

Map<Integer, String> map = ...
map.put(1, "1");
System.out.println(map.getOrDefault(1, "2")); // "1"
System.out.println(map.getOrDefault(2, "2")); // "2"
29
Jeffrey

Comme indiqué dans les commentaires:

Le concept de carte informatique de Guava a été remplacé par LoadingCache . De plus, Java 8 a introduit l'interface de carte Nice computeIfAbsent méthode par défaut qui ne rompt pas le contrat de carte et offre une évaluation paresseuse.


Guava a eu l'idée d'une "carte informatique" qui exécutera une fonction pour fournir une valeur si elle n'est pas présente. Il a été implémenté dans MapMaker.makeComputingMap; vous pouvez maintenant utiliser CacheBuilder - voir CacheBuilder.build pour plus de détails.

Cela peut être exagéré pour ce que vous recherchez - vous feriez peut-être mieux d'écrire une implémentation Map dont a a Map (en utilisant la composition plutôt que d'étendre une implémentation particulière), puis de renvoyer le défaut si ce n'est présent. Toute méthode autre que get pourrait probablement simplement déléguer à une autre carte:

public class DefaultingMap<K, V> implements Map<K, V>
{
    private final Map<K, V> map;
    private final V defaultValue;

    public DefaultingMap(Map<K, V> map, V defaultValue)
    {
        this.map = map;
        this.defaultValue = defaultValue;
    }

    @Override public V get(Object key)
    {
        V ret = map.get(key);
        if (ret == null)
        {
            ret = defaultValue;
        }
        return ret;
    }

    @Override public int size()
    {
        return map.size();
    }

    // etc
}
24
Jon Skeet

Similaire aux publications précédentes, sauf que vous pouvez simplement remplacer la méthode get si vous souhaitez modifier son comportement.

Map<String, List<String>> map = new LinkedHashMap<String, List<String>>() {
    public String get(Object key) {
        List<String> list = super.get(key);
        if (list == null && key instanceof String)
           super.put(key, list = new ArrayList<String>());
        return list;
    }
}; 
9
Peter Lawrey

Guava a une méthode pour faire exactement ce que vous voulez. C'est similaire à La réponse d'Argote .

Map<String, List<String>> myMap = ...
Functions.forMap(myMap, Arrays.asList("default", "value"));
7
Heath Borders

Il me semble que vous pouvez simplement écrire une fonction wrapper pour obtenir la valeur souhaitée et, si c'est null, renvoyer la valeur par défaut à la place.

Map<String, List<String>> myMap = new Map<String, List<String>>();

public List<String> myGet(String key) {
    List<String> ret = myMap.get(key);
    if(ret == NULL) {
        return defaultList(); // where defaultList is a function that generates your default list.
    }
    return ret;
}

Cela suppose que votre myMap est une variable globale (par souci de simplicité). Si ce n'est pas ce que vous voulez, vous pouvez également étendre Map ou HashMap à quelque chose comme MyMap et simplement inclure cette fonction supplémentaire. De cette façon, vous pouvez l'utiliser depuis n'importe quel endroit où un objet MyMap est instancié.

0
Argote