Search code examples
javajava-stream

Stream map returns null instead of stream


There is a list of elements of the Event class.

@Data
@AllArgsConstructor
public class Event {
    private Long id;
    private String nameRu;
    private String nameEn;
}

I want to get value of one field from an Event (any of the objects).

Event event1 = new Event(1, null, null);
Event event2 = new Event(2, null, null);
List<Event> events = new ArrayList<>();
events.add(event1);
events.add(event2);
int languageId = 1;

String name = events.stream().map(s -> {
                if (languageId == 1)
                    return s.getNameEn();
                else if (languageId == 2)
                    return s.getNameRu();
                else return s.getNameEn();
            }).findAny().orElse(null);

But if in each element this field has the value null, then stream.map() returns null (but it should return a stream of some number of nulls), and the findAny() call throws a NullPointerException.

Two questions:

  1. Why does the map() not return a stream if the map() is not terminal?
  2. How can I get the value of a field without exceptions?

Solution

  • According to JavaDoc this is expected behavior:

        /**
         * Returns an {@link Optional} describing some element of the stream, or an
         * empty {@code Optional} if the stream is empty.
         *
         * <p>This is a <a href="package-summary.html#StreamOps">short-circuiting
         * terminal operation</a>.
         *
         * <p>The behavior of this operation is explicitly nondeterministic; it is
         * free to select any element in the stream.  This is to allow for maximal
         * performance in parallel operations; the cost is that multiple invocations
         * on the same source may not return the same result.  (If a stable result
         * is desired, use {@link #findFirst()} instead.)
         *
         * @return an {@code Optional} describing some element of this stream, or an
         * empty {@code Optional} if the stream is empty
         * @throws NullPointerException if the element selected is null
         * @see #findFirst()
         */
        Optional<T> findAny();
    

    @throws NullPointerException if the element selected is null

    To fix this you must filter off null elements like this:

            .map(s -> ... )
            .filter(Objects::nonNull)
            .findAny()
            .orElse(null);