Как мы должны управлять потоком jdk8 для нулевых значений

Привет, разработчики Java,

Я знаю, что этот вопрос может быть немного in advance, поскольку JDK8 еще не выпущен (и не сейчас). Но я читал некоторые статьи о выражениях лямбда и особенно о части, связанной с новым API-интерфейсом коллекции известный как поток.

Вот пример, приведенный в статье Java Magazine (это алгоритм популяции выдра..):

Set<Otter> otters = getOtters();
System.out.println(otters.stream()
    .filter(o -> !o.isWild())
    .map(o -> o.getKeeper())
    .filter(k -> k.isFemale())
    .into(new ArrayList<>())
    .size());

Мой вопрос в том, что произойдет, если в середине внутренней итерации Set один из выдр имеет значение null?

Я бы ожидал, что будет вызвано исключение NullPointerException, но, может быть, я все еще придерживаюсь предыдущей парадигмы разработки (нефункционально), может кто-нибудь просветить меня как это должно быть обработано?

Если это действительно вызывает исключение NullPointerException, я считаю эту функцию довольно опасной и ее нужно будет использовать только как показано ниже:

  • Разработчик для обеспечения отсутствия нулевого значения (возможно, используя предыдущий .filter(o → o!= null))
  • Разработчик, чтобы гарантировать, что приложение никогда генерирование нулевого выдра или специальный объект NullOtter для обработки.

Каков наилучший вариант или любой другой вариант?

Спасибо!

Ответ 1

Современное мышление, похоже, должно "терпеть" нули, то есть разрешать их вообще, хотя некоторые операции менее терпимы и могут в конечном итоге бросать NPE. См. обсуждение нулей в списке рассылки группы экспертов Lambda Libraries, в частности this сообщение. После этого появился консенсус вокруг варианта № 3 (с заметным возражением от Дуга Ли). Так что да, озабоченность OP относительно трубопроводов, взорвающихся с NPE, действительно.

Не зря Тони Хоар упомянул нули как "Ошибка в миллиард долларов" . Работа с нулями - настоящая боль. Даже с классическими коллекциями (без учета лямбда или потоков) нули являются проблематичными. Как fge, упомянутых в комментарии, некоторые коллекции допускают null, а другие нет. С коллекциями, допускающими null, это вводит неоднозначность в API. Например, с Map.get() нулевой возврат указывает либо, что ключ присутствует, и его значение равно null, либо что ключ нет на месте. Нужно сделать дополнительную работу, чтобы устранить эти случаи.

Обычное использование для null - это обозначение отсутствия значения. Подход к рассмотрению этого предложения для Java SE 8 заключается в том, чтобы ввести новый тип java.util.Optional, который инкапсулирует наличие/отсутствие значения, наряду с поведением предоставления значения по умолчанию или бросанием исключения или вызовом функции и т.д., если значение отсутствует. Optional используется только новыми API, однако все остальное в системе все еще должно мириться с возможностью нулей.

Мой совет - избегать фактических нулевых ссылок в максимально возможной степени. Трудно видеть из приведенного примера, как может быть "нулевой" выдра. Но если это было необходимо, предложения OP о фильтрации нулевых значений или их сопоставлении с объектом-дозорным (

Ответ 2

Ответ Стюарта дает большое объяснение, но я хотел бы привести еще один пример.

Я столкнулся с этой проблемой при попытке выполнить reduce в потоке, содержащем нулевые значения (на самом деле это был LongStream.average(), что является типом сокращения). Поскольку average() возвращает OptionalDouble, я предположил, что Stream может содержать нули, но вместо этого было выбрано исключение NullPointerException. Это объясняется объяснением Стюарта null v. Empty.

Итак, как предлагает OP, я добавил фильтр следующим образом:

list.stream()
    .filter(o -> o != null)
    .reduce(..);

Или как тангенсы, указанные ниже, используйте предикат, предоставляемый Java API:

list.stream()
    .filter(Objects::nonNull)
    .reduce(..);

Из обсуждения в списке рассылки Стюарт связан: Брайан Гетц на нулях в потоках

Ответ 3

Хотя ответы на 100% правильны, небольшое предложение обработать нулевой регистр самого списка с Optional:

 List<String> listOfStuffFiltered = Optional.ofNullable(listOfStuff)
                .orElseGet(Collections::emptyList)
                .stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

Часть Optional.ofNullable(listOfStuff).orElseGet(Collections::emptyList) позволит вам хорошо разбираться в случае, когда listOfStuff имеет значение null и возвращает пустой список вместо отказа с помощью NullPointerException.

Ответ 4

Если вы просто хотите отфильтровать нулевые значения из потока, вы можете просто использовать ссылку на метод java.util.Objects.nonNull(Object). Из документации:

Этот метод существует для использования в качестве Predicate, filter(Objects::nonNull)

Например:

List<String> list = Arrays.asList( null, "Foo", null, "Bar", null, null);

list.stream()
    .filter( Objects::nonNull )  // <-- Filter out null values
    .forEach( System.out::println );

Это напечатает:

Foo
Bar

Ответ 5

Пример того, как избежать нулевого, например. используйте фильтр перед группировкойBy

Отфильтруйте нулевые экземпляры перед группировкойBy.

Вот пример
MyObjectlist.stream()
            .filter(p -> p.getSomeInstance() != null)
            .collect(Collectors.groupingBy(MyObject::getSomeInstance));