it-roy-ru.com

Как сделать карту Java 8 Stream непрерывно с нулевой проверкой

У меня есть этот кусок кода

Coverage mainCoverage = illus.getLifes().stream()
    .filter(Life::isIsmain)
    .findFirst()
    .orElseThrow(() -> new ServiceInvalidAgurmentGeneraliException(env.getProperty("MSG_002")))
    .getCoverages()  
    .stream() // <==may cause null here if list coverage is null
    .filter(Coverage::isMainplan)
    .findFirst()
    .orElseThrow(() -> new ServiceInvalidAgurmentGeneraliException(env.getProperty("MSG_002")));

это прекрасно работает, но я думаю, что это немного грязно и не охватывает все возможные null pointer exception (см. комментарий).

Я пытаюсь изменить этот код в

Coverage mainCoverage1 = illus.getLifes().stream()
    .filter(Life::isIsmain)
    .map(Life::getCoverages)
    .filter(Coverage::isMainplan) //<== cannot filter from list coverage to one main coverage
    ...

Кажется, после того, как я сопоставил жизнь с покрытием, это больше не список освещения. Итак, вопрос в том, как я могу преобразовать первый раздел в нулевой сейф и, возможно, сократить его?

8
Dang Nguyen

Life::getCoverages возвращает коллекцию, следовательно, фильтр Coverage::isMainplan не будет работать, вместо этого вы должны flatMap последовательности, возвращенные после .map(Life::getCoverages), затем применить операцию filter к Coverage:

Coverage mainCoverage = 
          illus.getLifes()
               .stream()
               .filter(Life::isIsmain)               
               .map(Life::getCoverages)
               //.filter(Objects::nonNull) uncomment if there can be null lists
               .flatMap(Collection::stream) // <--- collapse the nested sequences
               //.filter(Objects::nonNull) // uncomment if there can be null Coverage
               .filter(Coverage::isMainplan)
               .findFirst().orElse(...);

Я добавил несколько вещей в ваш код:

  1. Я добавил .filter(Objects::nonNull) after .map(Life::getCoverages) , который можно раскомментировать, учитывая, что возвращаемые элементы могут быть нулевыми.
  2. Я добавил .flatMap(Collection::stream) , который возвращает поток, состоящий из результатов замены каждого элемента этого потока содержимым отображенного потока, созданного путем применения предоставленной функции отображения к каждому элементу. 
  3. Я добавил еще одну функцию .filter(Objects::nonNull), которую вы можете раскомментировать, поскольку элементы, возвращаемые после flatMap могут быть нулевыми.
  4. Затем мы находимся на этапе, на котором мы можем применить .filter(Coverage::isMainplan) и, наконец, получить первый объект, удовлетворяющий критериям, через findFirst, а если его нет, предоставить значение по умолчанию через orElse.

Я бы посоветовал взглянуть на следующие блоги, чтобы ознакомиться с методом flatMap:

4
Aomine

Добавьте условие в filter, если список не нулевой и i.isIsmain, тогда только фильтр, вы можете использовать public static boolean isNull(Object obj) или public static boolean nonNull(Object obj)

Coverage mainCoverage = illus.getLifes().stream()
.filter(i->i.isIsmain && Objects.nonNull(i.getCoverages()))
.findFirst()
.orElseThrow(() -> new ServiceInvalidAgurmentGeneraliException(env.getProperty("MSG_002")))
.getCoverages()  
.stream() // <==may cause null here if list coverage is null
.filter(Coverage::isMainplan)
.findFirst()
.orElseThrow(() -> new ServiceInvalidAgurmentGeneraliException(env.getProperty("MSG_002")));
3
Deadpool

В первой части вашего кода вы можете вставить функцию filter(e -> e != null), чтобы убедиться, что значение List равно null, поэтому не будет выбрасывать NPE:

Coverage mainCoverage = illus.getLifes().stream()
         .filter(Life::isIsmain)
         .findFirst()
         .orElseThrow(() -> new ServiceInvalidAgurmentGeneraliException(env.getProperty("MSG_002")))
         .getCoverages()  
         .filter(e -> e != null) //<=== Filter out all null values
         .stream()
         .filter(Coverage::isMainplan)
         .findFirst()
         .orElseThrow(() -> new ServiceInvalidAgurmentGeneraliException(env.getProperty("MSG_002"))

Проблема с вашим вторым фрагментом кода в том, что я предполагаю, что Life::getCoverages возвращает Collection, а не отдельные объекты Coverage, поэтому вы не можете вызывать Coverage::isMainplan для него

2
GBlodgett

Вы можете попытаться инкапсулировать полученный Collection<Coverage> в Optional<Collection<Coverage>> так, чтобы вы могли отображать в null safe способом.

final Supplier<ServiceInvalidAgurmentGeneraliException> customExceptionThrower = () -> new ServiceInvalidAgurmentGeneraliException(env.getProperty("MSG_002"));

final Collection<Coverage> firstMainLifeCoverages = illus.getLifes().stream()
    .filter(Life::isIsmain)
    .findFirst()
    .orElseThrow(customExceptionThrower)
    .getCoverages();

Optional.ofNullable(firstMainLifeCoverages)
    .map(Collection::stream)
    .orElseThrow(customExceptionThrower)
    .filter(Coverage::isMainplan)
    .findFirst()
    .orElseThrow(customExceptionThrower);
0
HPH