2011-11-30 13 views
32

Sono malato del seguente schema:Java map.get (chiave) - automaticamente mettere put (chiave) e tornare se la chiave non esiste?

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

Questo esempio solo una minima del codice aggiuntivo da scrivere quando si hanno nidificato le mappe per rappresentare una struttura multi-dimensionale.

Sono sicuro che qualcosa esiste da qualche parte per evitare questo, ma i miei sforzi Googling ceduta niente di rilevante. Eventuali suggerimenti?

+0

Per curiosità, l'oggetto che vuoi mettere è solo un oggetto o il tipo varia? Inoltre, è già stato creato o dovrebbe essere creato solo se nessun oggetto esiste già? –

+0

Il tipo è noto al momento della compilazione. Di solito è una stringa da mappare (per mappare) * su intero. –

risposta

24

Il

java.util.concurrent.ConcurrentMap 

e da Java 8

Java.util.Map 

ha

putIfAbsent(K key, V value) 

che restituisce il valore mappato a chiave o inserisce il valore dato e null se nessun valore è mappato per la chiave.

Se avete bisogno di valutazione pigra del valore c'è

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

Restituisce: il valore precedente associato alla chiave specificata o null se non vi era alcuna associazione per la chiave – user802421

+0

Perfetto! Questo è quello che stavo cercando. Grazie, Roger. –

+5

Nota che questo ti obbliga a creare un nuovo oggetto, anche se ce n'è già uno disponibile nella mappa. Se crei solo un 'nuovo oggetto()', questo potrebbe essere trascurabile. –

3

Il problema di questo modello è che si sarebbe necessario definire in qualche modo il valore che dovrebbe essere utilizzato nel caso in cui le get() restituisce null.

certamente ci sono le librerie là fuori e IIRC ci sono anche alcune collezioni più recenti che lo fanno, ma purtroppo non mi ricordo che quelli erano.

Tuttavia, si potrebbe scrivere un metodo di utilità voi stessi, a patto di avere un metodo standard per creare i nuovi valori. Qualcosa del genere potrebbe funzionare:

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

Si noti che è necessario eseguire le eccezioni di riflessione o gestirle nel metodo. Inoltre, ciò richiede che lo valueClass fornisca un costruttore senza argomenti. In alternativa, potresti semplicemente passare il valore predefinito che dovrebbe essere usato.

+0

Questo è uno snippet di codice utile. Grazie. Non sapevo come farlo con i generici. –

1

MODIFICA: Si noti che la funzione menzionata di seguito è deprecata a lungo e al suo posto è necessario utilizzare CacheBuilder.

Il Guava biblioteca ha un "calcolo mappa", vedi MapMaker.makeComputingMap(Function).

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

Se avete bisogno della funzione più volte, estrarlo in una classe di utilità, e quindi creare la mappa come questo (dove MyFunctions.NEW_OBJECT è l'istanza funzione statica):

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

'MapMaker' di Google Guava è morto. Rif: http://code.google.com/p/guava-libraries/wiki/MapMakerMigration – kevinarpe

+0

Capisco che gli utenti di Guava credano che il 'calcolo automatico 'Map.get()' non funziona, perché interrompe il contratto 'Mappa'. Se si utilizza la mappa di calcolo solo internamente, dovrebbe essere semplice utilizzare invece un 'LoadingCache'. Riesco a vedere il loro punto di una catastrofe in attesa di accadere quando si condivide una mappa di calcolo con codice ignaro. Per quel caso d'uso, si dovrebbe usare una funzione di utilità 'safeGet()' come quella data da Thomas nella sua risposta. –

0

Forse sono non vedere l'intero problema, ma come usare l'ereditarietà o la composizione per aggiungere questo comportamento all'oggetto Mappa?

+0

Questa è stata la cosa migliore che ho trovato, sì. Grazie per la risposta. –

3

È possibile utilizzare MutableMap e getIfAbsentPut() da Eclipse Collections che restituisce il valore mappato alla chiave o inserisce il valore dato e restituisce il valore dato se nessun valore è mappato al tasto.

È possibile utilizzare un riferimento metodo per creare un nuovo Object:

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

Oppure è possibile creare direttamente un nuovo Object:

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

Nel primo esempio, verrà creato l'oggetto solo se non vi è alcun valore associato alla chiave.

Nel secondo esempio, l'oggetto verrà creato indipendentemente.

Nota: contribuisco alle raccolte Eclipse.

1

Se in ogni caso è necessario per ottenere un dato di default nella vostra mappa se non è esistente

map.getOrDefault(key, defaultValue); 

javadocs

+1

Interessante, ma questo non lo inserisce nella mappa come richiesto dall'OP –

Problemi correlati