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

У меня есть этот фрагмент кода

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

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

Я предполагаю, что Life::getCoverages возвращает Collection, а не отдельные объекты Coverage.

GBlodgett 25.12.2018 02:42
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
12
1
15 530
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

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

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

В первой части вашего кода вы можете вставить filter(e -> e != null), чтобы не быть уверенным, что List равен нулю, он не выдаст 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 на нем.

вы все еще ведете потоковую передачу через Life#getCoverages, что может привести к выбросу NPE.

HPH 25.12.2018 09:55

@HPH Я отфильтровываю все нулевые значения, поэтому, даже если List::getCoverages возвращает null, это не будет проходить поверх нулевых значений

GBlodgett 26.12.2018 20:05

вы все еще ошибаетесь, вы не можете передавать поток через коллекцию null, коллекция в данном случае - это life.getCoverages(), где life - это первый ненулевойLife, чей isIsmain() возвращает true.

HPH 26.12.2018 23:06

Добавьте условие в 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")));

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

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

Приведенный вами пример и приведенные здесь ответы нарушают некоторые принципы программирования в чистом функциональном стиле.

Во-первых, вам не следует смешивать действия терминала и снова создавать потоки в одной и той же цепочке / конвейере кода. Как в вашем примере stream (). findFirst (). orElseThrow (..). stream (). otherActions. Это действительно не очень хорошая практика и подвержена ошибкам. В идеале цепочка вызовов Java Stream API должна работать с одним Stream. Так будет легче следить за своим кодом и рассуждать о нем.

Во-вторых, вы упомянули, что этот бит может поразить Null Pointer exc:

.getCoverages()  
.stream() // <==may cause null here if list coverage is null

если getCoverages () должен возвращать коллекцию, она никогда не должна возвращать null, вместо этого всегда возвращать пустую коллекцию.

Разбейте код на отдельные логические части и дайте ему несколько значимых имен:

Coverage mainCoverage = illus.getLifes().stream()
    .filter(Life::isIsmain)
    .findFirst()
    .orElseThrow(() -> new ServiceInvalidAgurmentGeneraliException(env.getProperty("MSG_002")));

Coverage mainplan = mainCoverage.getCoverages().stream()
    .filter(Coverage::isMainplan)
    .findFirst()
    .orElseThrow(() -> new ServiceInvalidAgurmentGeneraliException(env.getProperty("MSG_002")));

Это выглядит намного лучше, чем ваша отправная точка. Надеюсь, это поможет.

хотя ваш ответ не дает прямого ответа на мой вопрос, но +1, чтобы хорошо объяснить и показать мне настоящую проблему моего кода.

Dang Nguyen 03.04.2019 04:14

Другие вопросы по теме