web-dev-qa-db-fra.com

Java map.get (key) - automatiquement mettre (key) et retourner si la clé n'existe pas?

J'en ai marre du schéma suivant:

value = map.get(key);
if (value == null) {
    value = new Object();
    map.put(key, value);
}

Cet exemple n'égratigne la surface du code supplémentaire à écrire que lorsque vous avez des cartes imbriquées pour représenter une structure multidimensionnelle.

Je suis sûr que quelque chose existe pour éviter cela, mais mes efforts de recherche sur Google n'ont rien donné de pertinent. Des suggestions?

48
Sridhar Sarnobat

Le

Java.util.concurrent.ConcurrentMap 

et de Java 8

Java.util.Map

a

putIfAbsent(K key, V value) 

qui renvoie la valeur mappée à la clé ou insère la valeur donnée et null si aucune valeur n'est mappée pour la clé.

Si vous avez besoin d'une évaluation paresseuse de la valeur,

computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)
43
Roger Lindsjö

Java 8 ajoute la méthode Nice à Map : compute , computeIfPresent , computeIfAbsent

Pour réaliser ce dont vous avez besoin:

Object existingOrCreated = map.computeIfAbsent(key, (k) -> new Object());
32
iTake

Le problème avec ce modèle est que vous devez en quelque sorte définir la valeur à utiliser dans le cas où la get() retourne null.

Il y a certainement des bibliothèques là-bas et l'IIRC, il y a aussi des collections plus récentes qui font ça, mais malheureusement je ne me souviens pas lesquelles.

Cependant, vous pouvez écrire vous-même une méthode utilitaire, à condition que vous disposiez d'un moyen standard de créer les nouvelles valeurs. Quelque chose comme ça pourrait fonctionner:

public static <K, V> V safeGet(K key, Map<K,V> map, Class<V> valueClass) throws /*add exceptions*/ {
  V value = map.get(key);
  if( value == null ) {
    value = valueClass.newInstance();
    map.put( key, value );
  }   

  return value;
} 

Notez que vous devez soit lever les exceptions de réflexion, soit les gérer dans la méthode. De plus, cela nécessite que valueClass fournisse un constructeur sans argument. Alternativement, vous pouvez simplement transmettre la valeur par défaut à utiliser.

3
Thomas

Vous pouvez utiliser MutableMap et getIfAbsentPut() from Eclipse Collections qui renvoie la valeur mappée à la clé ou insère la valeur donnée et renvoie la valeur donnée si aucune valeur est mappé à la clé.

Vous pouvez soit utiliser une référence de méthode pour créer un nouveau Object:

MutableMap<String, Object> map = Maps.mutable.empty();
Object value = map.getIfAbsentPut("key", Object::new);

Ou vous pouvez directement créer un nouveau Object:

MutableMap<String, Object> map = Maps.mutable.empty();    
Object value = map.getIfAbsentPut("key", new Object());

Dans le premier exemple, l'objet sera créé uniquement s'il n'y a aucune valeur mappée à la clé.

Dans le deuxième exemple, l'objet sera créé malgré tout.

Remarque: je suis un contributeur aux collections Eclipse.

3
Nikhil Nanivadekar

Si dans tous les cas, vous devez obtenir des données par défaut dans votre carte si elles n'existent pas

map.getOrDefault(key, defaultValue);

javadocs

1
letthefireflieslive

EDIT: Notez que la fonctionnalité mentionnée ci-dessous est obsolète depuis longtemps, et un CacheBuilder doit être utilisé à la place.

La bibliothèque Guava possède une " carte informatique ", voir MapMaker.makeComputingMap (Fonction) .

Map<String, Object> map = new MapMaker().makeComputingMap(
    new Function<String, Object>() {
      public String apply(Stringt) {
        return new Object();
      }
  });

Si vous avez besoin de la fonction plusieurs fois, extrayez-la dans une classe utilitaire, puis créez la carte comme ceci (où MyFunctions.NEW_OBJECT est l'instance de fonction statique):

Map<String, Object> map = new MapMaker()
    .makeComputingMap(MyFunctions.NEW_OBJECT);
1
Christian Semrau

Peut-être que je ne vois pas tout le problème, mais que diriez-vous d'utiliser l'héritage ou la composition pour ajouter ce comportement à l'objet Map?

0
Trein