API потока Java 8 или использование Else - PullRequest
0 голосов
/ 04 декабря 2018

Я пытаюсь отфильтровать список, затем сопоставить его и использовать 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 в этом случае?

Ответы [ 5 ]

0 голосов
/ 06 декабря 2018

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

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

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

public static <K,V> Map<K,V> emptyIfNull(Map<K,V> map) {
    return map == null ? Collections.<K,V>emptyMap() : map;
}
0 голосов
/ 04 декабря 2018

Использование 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.


Второй вариант: Inline 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 сделано для объектов по умолчанию, поиск или создание которых является тяжелым.

0 голосов
/ 04 декабря 2018

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

Метод 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.

0 голосов
/ 04 декабря 2018

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

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>>.

0 голосов
/ 04 декабря 2018

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

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())
;
...