Использование Java 8 Необязательно для безопасного перемещения по карте
У меня есть Map<String, Map<String, String>> myMap
в моем классе Java 8. Мне нужно перейти к строке String как myMap['keyA']['keyB']
, возвращая null
, если в корреляционной карте не существует 'keyA'
или 'keyB'
.
В groovy я бы использовал myMap?.keyA?.keyB
и делал с ним. Я понимаю, что Java 8 Optional<T>
приводит подобное поведение в java. Есть ли способ использовать это новое поведение, чтобы кратко имитировать функциональность groovy? Если нет, есть ли еще один краткий способ получить это поведение в Java 8, или я все еще придерживаюсь сложного процедурного кода?
Ответы
Ответ 1
Вы можете использовать Optional
ofNullable
метод для создания Optional
, который может или не может представлять значение null
. Затем вы можете использовать метод map
, который с Function
сопоставляет результат с новым значением, если значение не было уже null
.
Здесь я поставлю Function
как выражение лямбда, чтобы получить значение из второго map
, используя второй ключ.
Optional<String> result = Optional.ofNullable(myMap.get("keyA")).map(m -> m.get("keyB"));
Оттуда вы можете увидеть, имеет ли значение Optional
значение с isPresent()
, и если да, получите его с помощью get()
.
Тестирование:
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));
}
Код вызова:
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"));
Вывод:
Optional[two]
Optional.empty
Optional.empty
Ответ 2
String valueOrNull = Optional.ofNullable(myMap.get("keyA"))
.map(x -> x.get("keyB"))
.orElse(null);
Сначала он завершает результаты первого поиска в Optional, который действует как монада. Если вы добавите третий слой (myMap.?keyA.?keyB.?keyC
), он будет выглядеть так:
String valueOrNull = Optional.ofNullable(myMap.get("keyA"))
.map(x -> x.get("keyB"))
.map(x -> x.get("keyC"))
.orElse(null);
Ответ 3
Интересный вопрос.
Вы можете использовать рекурсию.
/**
* 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;
}
Это будет работать с Java >= 9 (вы можете легко адаптировать его к предыдущим версиям).
Бонус (требуется Гуава):
@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"));
}