2011-11-30 6 views
32

Estoy enfermo de la siguiente distribución:Java map.get (clave) - ¿pone automáticamente (clave) y regresa si la clave no existe?

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

Este ejemplo sólo roza la superficie del código extra para ser escrito cuando haya anidado mapas para representar una estructura multidimensional.

Estoy seguro de que existe algo en alguna parte para evitar esto, pero mis esfuerzos en Google no arrojaron nada relevante. ¿Alguna sugerencia?

+0

Por curiosidad, el Objeto que desea colocar, ¿es solo un Objeto, o variará el tipo? Además, ¿ya está creado o solo debe crearse si ya no existe ningún objeto? –

+0

El tipo es conocido en tiempo de compilación. Por lo general, es una Cadena de Asignar (a un Mapa) * a Entero. –

Respuesta

24

El

java.util.concurrent.ConcurrentMap 

y de Java 8

Java.util.Map 

tiene

putIfAbsent(K key, V value) 

que devuelve el valor asignado a la clave o inserta el valor determinado y un valor nulo si no hay ningún valor mapeado para la clave.

Si necesita evaluación perezosa del valor no es

computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction) 
+3

Devuelve: el valor anterior asociado con la clave especificada, o nulo si no hubo una asignación para la clave – user802421

+0

¡Perfecto! Eso es lo que estaba buscando. Gracias, Roger. –

+5

Tenga en cuenta que esto le obliga a crear un nuevo objeto, incluso si ya hay uno disponible en el mapa. Si realmente solo creas un 'nuevo Objeto()', esto puede ser insignificante. –

3

El problema con este modelo es que habría que definir de alguna manera el valor que se debe utilizar en el caso de los get() vuelve nula.

Ciertamente hay bibliotecas y IIRC también hay algunas colecciones más nuevas que lo hacen, pero desafortunadamente no recuerdo cuáles eran.

Sin embargo, usted mismo puede escribir un método de utilidad, siempre que tenga una forma estándar de crear los nuevos valores. Algo como esto podría funcionar:

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; 
} 

Tenga en cuenta que habías bien tiene que tirar las excepciones de reflexión o manejarlos en el método. Además, esto requiere que valueClass proporcione un constructor sin argumentos. Alternativamente, simplemente puede pasar el valor predeterminado que se debe utilizar.

+0

. Es un fragmento de código útil. Gracias. No sabía cómo hacerlo con genéricos. –

1

EDITAR: Tenga en cuenta que la característica que se menciona a continuación está largamente obsoleta, y en su lugar debe usarse un CacheBuilder.

La Guayaba biblioteca tiene un "mapa de computación ", véase MapMaker.makeComputingMap(Function).

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

Si necesita la función varias veces, extraerlo en una clase de utilidad, y luego crear el mapa del modo siguiente (MyFunctions.NEW_OBJECT es la instancia de función estática):

Map<String, Object> map = new MapMaker() 
    .makeComputingMap(MyFunctions.NEW_OBJECT); 
+0

'MapMaker' de Google Guava está muerto. Ref: http://code.google.com/p/guava-libraries/wiki/MapMakerMigration – kevinarpe

+0

Entiendo que la gente de Guava piensa que una autocomputación 'Map.get()' es mala, porque rompe el contrato 'Map'. Si usa el mapa informático solo internamente, debería ser fácil usar un 'LoadingCache' en su lugar. Puedo ver el punto de una catástrofe esperando a suceder cuando se comparte un mapa informático con un código desprevenido. Para ese caso de uso, uno debe usar una función de utilidad 'safeGet()' como la dada por Thomas en su respuesta. –

0

Tal vez soy no ver todo el problema, pero ¿qué hay de usar la herencia o la composición para agregar este comportamiento al objeto Map?

+0

Esa fue la siguiente mejor cosa que encontré, sí. Gracias por la respuesta. –

3

Puede utilizar MutableMap y getIfAbsentPut() de Eclipse Collections que devuelve el valor asignado a la tecla o inserta el valor dado y devuelve el valor dado si no hay valor se asigna a la tecla.

También se puede usar un método de referencia para crear un nuevo Object:

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

O puede crear directamente un nuevo Object:

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

En el primer ejemplo, se crea el objeto solo si no hay un valor asignado a la clave.

En el segundo ejemplo, el objeto se creará independientemente.

Nota: Soy colaborador de Eclipse Collections.

1

Si en cualquier caso, es necesario obtener un conjunto de datos por defecto en su mapa si no es existente

map.getOrDefault(key, defaultValue); 

javadocs

+1

Interesante, pero esto no lo pone en el mapa como se lo pidió OP –

Cuestiones relacionadas