it-swarm.com.ru

API потока Java 8 или использование Else

Я пытаюсь отфильтровать список, затем сопоставить его и использовать orElse, если null, а затем собрать его обратно в список. Теперь я могу добиться этого следующим образом:

return users.stream()
    .filter(user -> id.equals(user.getId()))
    .map(
        user -> {
            if(user.getData() != null) {
                return user.getData();
            }
            return Collections.emptyMap();
        }
    )
    .collect(Collectors.toList());

Но вопрос в том, как я могу улучшить эту структуру и почему я не могу использовать orElse в этом случае?

16
Mykyta Bezverkhyi

Это может быть более читаемо с помощью троичного условного оператора:

return users.stream()
    .filter(user -> id.equals(user.getId()))
    .map(
        user -> (user.getData() != null) 
        ? user.getData() 
        : emptyMap()
    )
    .collect(Collectors.toList())
;

Чтобы использовать orElse, вам нужно создать Optional, который обертывает user.getData(). Я не уверен, что это хорошая идея.

Если вы настаиваете на использовании orElse (или даже лучше, orElseGet, чтобы избежать оценки emptyMap(), когда она не требуется), это может выглядеть так:

return users.stream()
    .filter(user -> id.equals(user.getId()))
    .map(
        user -> Optional.ofNullable(
            user.getData()
        ).orElseGet(
            () -> emptyMap()
        )
    )
    .collect(Collectors.toList())
;
14
Eran

Как я отмечал в комментариях, я очень сомневаюсь, что вы можете просто искать следующее

users
    .stream()
    .filter(
        user -> id.equals(user.getId()) 
        && (user.getData() != null)
    )
    .map(User::getData)
    .collect(Collectors.toList())
;

Но тогда вопрос недостаточно ясен, чтобы сказать, каков тип возвращаемого значения вашего оператора или что emptyMap используется в вашем коде! Поэтому я очень сомневаюсь, если вам вообще нужен API Optional для этой операции.

Примечание. Вышеупомянутое решение предполагает, что emptyMap - это Collections.emptyMap, и я не уверен, почему нужно собирать данные в структуре данных, которая обозначается как List<Map<K,V>>.

4
nullpointer

Как я могу сделать эту структуру лучше 

Способ 1:

return users.stream()
    .filter(user -> id.equals(user.getId()))
    .map(
        user -> (user.getData() != null)
        ? user.getData() 
        : emptyMap()
    )
    .collect(Collectors.toList())
;

Способ 2:

Сделайте так, чтобы ваш getData возвращал Optional: user -> user.getData().orElse(emptyMap())

Способ 3:

Как сказал @Eran: Optional.ofNullable, тогда используйте orElse(emptyMap()) как выше: user -> Optional.ofNullable(user.getData()).orElse(emptyMap())

Почему я не могу использовать orElse в этом случае?

Не уверен, что orElse вы имеете в виду

  1. Если user.getData() возвращает null, его следует поместить в Optional для вызова orElse.

  2. Функция findAny().orElse потока работает с самим результатом потока. Но здесь вам нужно проверить, существует ли user.getData(). Таким образом, вы не можете напрямую использовать результат потока orElse.

1
shawn

Используйте Objects::requireNonNullElse !

Я бы посоветовал две вещи, чтобы сделать код более читабельным. Однако я не стал бы искусственно вводить Optional.


Первый вариант: Objects::requireNonNullElse в отдельном методе

List<Map<?, ?> bar() {
    //...

    return users.stream()
                .filter(user -> id.equals(user.getId()))
                .map(User::getData)
                .map(Foo::nullSafeMap)
                .collect(Collectors.toList());
}

private static Map<?, ?> nullSafeMap(final Map<?, ?> map) {
    return Objects.requireNonNullElse(map, Collections.emptyMap());
}

Здесь вы должны использовать Objects::requireNonNullElse, который возвращает объект, переданный в первом параметре, если это не null, и объект, переданный в качестве второго параметра, если первым параметром является null. Наличие отдельного метода позволяет передавать ссылку на метод в Stream::map, но требует, чтобы вы сначала сопоставили экземпляры User с их данными Map


Второй вариант: встроенный Objects::requireNonNullElse

List<Map<?, ?> bar() {
    //...

    return users.stream()
                .filter(user -> id.equals(user.getId()))
                .map(User::getData)
                .map(map -> Objects.requireNonNullElse(map, Collections.emptyMap()))
                .collect(Collectors.toList());
}

Если вы не хотите, чтобы отдельный метод выполнял только эту единственную задачу, вы можете встроить метод и при желании даже удалить первое отображение в пользу .map(user -> Objects.requireNonNullElse(user.getData(), Collections.emptyMap())), но я бы посоветовал против этого. Не бойтесь делать несколько вызовов Stream::map, если это делает код более читабельным.


Заключение

Я бы предпочел первый вариант, так как он делает код очень читабельным: вы знаете, что сопоставляете экземпляры User с данными, а затем делаете эти данные безопасными. 

Второй вариант в порядке, но страдает от очень длинной строки, которая может сбить с толку на первый взгляд. Это намного лучше, чем многострочная лямбда. Я бы во что бы то ни стало избегал многострочных лямбд и всегда извлекал их содержимое в отдельный метод.

Одна вещь, которую вы могли бы улучшить - это имя метода nullSafeMap, чтобы избежать путаницы между Stream::map и Java.util.Map

Обратите внимание, что вам не нужно использовать Objects::requireNonNullElseGet , так как Collections::emptyMap - это легкий метод, который только приводит и возвращает константу:

public static final <K,V> Map<K,V> emptyMap() {
    return (Map<K,V>) EMPTY_MAP;
}

Objects::requireNonNullElseGet сделан для объектов по умолчанию, поиск или создание которых является тяжелым.

1
Marv

Если у вас уже есть Apache Collections 4 как зависимость:

return users
    .stream()
    .filter(user -> id.equals(user.getId()))
    .map(User::getData)
    .map(MapUtils::emptyIfNull)
    .collect(Collectors.toList())
;

Если вы не используете Apache Collections, просто определите вспомогательный метод:

public static <K,V> Map<K,V> emptyIfNull(Map<K,V> map) {
    return map == null ? Collections.<K,V>emptyMap() : map;
}
0
Illya Kysil