Я бы сказал, что ваш путь почти уже самый элегантный, я бы внес лишь небольшие косметические изменения и заменил e -> e.getKey()
в вашем коллекторе на Entry::getKey
.Это лишь небольшое изменение, но оно передает ваше намерение лучше, чем другая лямбда.
Map<P, Optional<Q>> map = new HashMap<>();
Map<P, Q> sparseMap = map.entrySet().stream()
.filter(e -> e.getValue().isPresent())
.collect(Collectors.toMap(Entry::getKey, e -> e.getValue().get()));
Почему другие решения не лучше / элегантнее?
Потому что они не являются более краткими, и они снова попадают в ловушку не объявления того, что вы хотите сделать, а как , что часто встречается в процедурномстили, но не так много в функциональных.
Если вы посмотрите на приведенный выше код, он в значительной степени говорит само за себя и имеет хороший поток к нему.Сначала у вас есть не разреженная карта с Optional
s, затем объявляется разреженная карта без Optional
s, а затем описывается преобразование первой карты в последнюю.Это также не имеет побочных эффектов.Разреженная карта назначается только тогда, когда сборщик фактически завершен.
Если вы посмотрите на другие решения, те инвертируют логический поток и используют процедурный способ мышления:
Map<P, Optional<Q>> map = [....];
Map<P, Q> sparseMap = new HashMap<>();
map.forEach((key, opt) -> opt.ifPresent(value -> sparseMap.put(key, value)));
Это лишь незначительно короче:
Map<P, Optional<Q>> map = [....];
Map<P, Q> sparseMap = new HashMap<>();
for (Entry<P, Optional<Q>> e : map.entrySet()) e.getValue().ifPresent(value -> sparseMap.put(key, value))
Вы экономите несколько символов из-за логического вывода типа, но, в конце концов, если вы отформатируете их разумно, оба решения foreach
требуют 4 LOC,поэтому они не ниже функционального.Они не яснее, либо.На напротив они полагаются на побочных эффектов на другой карте.Это означает, что во время вычислений вы получаете частично построенную разреженную карту, назначенную вашей переменной.При функциональном решении карта назначается только тогда, когда она правильно построена.Это всего лишь небольшая мелочь, и в этом случае, скорее всего, она не вызовет проблем, но следует учитывать ее и в других ситуациях, когда это может стать актуальным (например, когда задействован параллелизм), особенно когда другая карта не являетсялокальная переменная, но поле - или, что еще хуже, переданное откуда-то еще.
Кроме того, функциональный подход масштабируется лучше - переключение на параллельный поток, если у вас много данных, тривиально, преобразование foreach
-Подход к параллели требует переписывания на функциональный filter
/ collect
подход в любом случае.Это не относится к таким легковесным операциям (на самом деле, не делайте этого здесь, вероятно, медленнее), но в других ситуациях это может быть желательной характеристикой.
На мой взгляд, с использованием функционала * 1042Подход * / collect
предпочтительнее, чем использование процедурного foreach
, потому что вы учитесь использовать хорошие привычки.Но имейте в виду, что «элегантность» часто находится в глазах смотрящего.Для меня более «элегантный» способ - это правильный функциональный способ, который не имеет побочных эффектов.YMMV.