2015-08-12 1 views
10

Ho Map<String, Map<String, String>> myMap nella mia classe Java 8. Devo navigare su una foglia Stringa come myMap['keyA']['keyB'], restituendo null se 'keyA' o 'keyB' non esiste nella mappa correlata.Utilizzo di Java 8 Facoltativo per l'attraversamento sicuro della mappa

In groovy vorrei usare myMap?.keyA?.keyB e averci finito. Capisco che Java 8's Optional<T> porti un comportamento simile in java. C'è un modo per usare questo nuovo comportamento per simulare in modo conciso la funzionalità groovy? In caso contrario, esiste un altro modo conciso per ottenere questo comportamento in Java 8 o sono ancora bloccato con un codice procedurale elaborato?

risposta

4

È possibile utilizzare Optional's ofNullable method per creare un Optional che possono o non possono rappresentare un valore null. Quindi è possibile utilizzare the map method che, con un valore Function, associa il risultato a un nuovo valore se il valore non era già null.

Qui, fornisco un Function come espressione lambda per ottenere il valore dal secondo Map utilizzando la seconda chiave.

Optional<String> result = Optional.ofNullable(myMap.get("keyA")).map(m -> m.get("keyB")); 

Da lì si può vedere se il Optional ha un valore con isPresent(), e in tal caso, ottenere con get().

Testing:

public static Optional<String> method(Map<String, Map<String, String>> map, 
    String key1, String key2) 
{ 
    return Optional.ofNullable(map.get(key1)).map(m -> m.get(key2)); 
} 

Codice Calling:

Map<String, Map<String, String>> myMap = new HashMap<>(); 
Map<String, String> inner = new HashMap<>(); 
inner.put("one", "two"); 
myMap.put("three", inner); 
System.out.println(method(myMap, "three", "one")); 
System.out.println(method(myMap, "three", "dne")); 
System.out.println(method(myMap, "dne", "dne")); 

uscita:

Optional[two] 
Optional.empty 
Optional.empty 
5
String valueOrNull = Optional.ofNullable(myMap.get("keyA")) 
          .map(x -> x.get("keyB")) 
          .orElse(null); 

primo luogo, esso avvolge i risultati della prima ricerca in un Optional, che agisce come una monade. Se si aggiunge un terzo strato (myMap.?keyA.?keyB.?keyC), sarebbe simile a questa:

String valueOrNull = Optional.ofNullable(myMap.get("keyA")) 
          .map(x -> x.get("keyB")) 
          .map(x -> x.get("keyC")) 
          .orElse(null); 
1

Domanda interessante.

È possibile considerare l'utilizzo della ricorsione.

/** 
* Finds the value of a node in nested maps. 
* @return leaf value or null if none 
*/ 
public <K, V> V getValueFromKeys(Map<K, V> map, K... keys) { 
    V value = map.getOrDefault(keys[0], null); 
    if (keys.length == 1) return value; 
    if (value instanceof Map) { 
     K[] remainingKeys = Arrays.copyOfRange(keys, 1, keys.length); 
     return getValueFromKeys((Map<K, V>) value, remainingKeys); 
    } 
    return null; 
} 

Funzionerà con Java> = 9 (è possibile adattarlo facilmente alle versioni precedenti).

Bonus (ha bisogno di Guava):

@Test 
public void getValueFromKeys_level1() { 
    Map<String, String> mapLevel1 = ImmutableMap.of("key1", "value1"); 

    assertEquals("value1", getValueFromKeys(mapLevel1, "key1")); 
    assertNull(getValueFromKeys(mapLevel1, null)); 
    assertNull(getValueFromKeys(mapLevel1, "")); 
    assertNull(getValueFromKeys(mapLevel1, "wrong")); 
    assertNull(getValueFromKeys(mapLevel1, "key1", "wrong")); 
} 

@Test 
public void getValueFromKeys_level2() { 
    Map<String, Map<String, String>> mapLevel2 = ImmutableMap.of("key1", ImmutableMap.of("subkey1", "value1")); 

    assertEquals("value1", getValueFromKeys(mapLevel2, "key1", "subkey1")); 
    assertNull(getValueFromKeys(mapLevel2, null)); 
    assertNull(getValueFromKeys(mapLevel2, "")); 
    assertNull(getValueFromKeys(mapLevel2, "wrong")); 
    assertNull(getValueFromKeys(mapLevel2, "key1", "wrong")); 
    assertNull(getValueFromKeys(mapLevel2, "key1", "subkey1", "wrong")); 
    assertTrue(getValueFromKeys(mapLevel2, "key1") instanceof Map); 
} 

@Test 
public void getValueFromKeys_level3() { 
    Map<String, Map<String, Map<String, String>>> mapLevel3 = ImmutableMap.of("key1", ImmutableMap.of("subkey1", ImmutableMap.of("subsubkey1", "value1"))); 

    assertEquals("value1", getValueFromKeys(mapLevel3, "key1", "subkey1", "subsubkey1")); 
    assertNull(getValueFromKeys(mapLevel3, null)); 
    assertNull(getValueFromKeys(mapLevel3, "")); 
    assertNull(getValueFromKeys(mapLevel3, "wrong")); 
    assertNull(getValueFromKeys(mapLevel3, "key1", "wrong")); 
    assertNull(getValueFromKeys(mapLevel3, "key1", "subkey1", "wrong")); 
    assertNull(getValueFromKeys(mapLevel3, "key1", "subkey1", "subsubkey1", "wrong")); 
} 
Problemi correlati