Handling null in java streams with optional

Viewed 2012

What would be the best way of handling null if we have senario like below

//mocking for demonstraton  
studentsByCourseRoster.setUsers(null);

studentsByCourseRoster.getUsers().stream().forEach(user -> {
    final UserDTOv2 userDTO = new UserDTOv2();
    userDTO.populateUserDataFromUserDTO(user, groupedUsers);
    users.add(userDTO);
});
5 Answers

If you want to retain the single statement structure, you could use Optional.ofNullable and replace null with an empty list:

Optional.ofNullable(studentsByCourseRoster.getUsers())
        .orElse(Collections.emptyList())
        .forEach(user -> {
                     final UserDTOv2 userDTO = new UserDTOv2();
                     userDTO.populateUserDataFromUserDTO(user, groupedUsers);
                     users.add(userDTO);
         });

With a slight modification to Mureinik's answer I would instead use :

List<UserDTOv2> users = Optional.ofNullable(studentsByCourseRoster.getUsers())
        .orElse(Collections.emptyList())
        .stream()
        .map(user -> {
            UserDTOv2 userDTO = new UserDTOv2();
            userDTO.populateUserDataFromUserDTO(user, groupedUsers);
            return userDTO;
        }).collect(Collectors.toList());

Using Stream.ofNullable in Java 9

List<UserDTOv2> users = Stream.ofNullable(studentsByCourseRoster.getUsers())
        .map(user -> {
            UserDTOv2 userDTO = new UserDTOv2();
            userDTO.populateUserDataFromUserDTO(user, groupedUsers);
            return userDTO;
        }).collect(Collectors.toList());

What would be the best way of handling null if we have senario like below

The simplest solution to that under your current design is to do so with an if statement.

That is not saying it's the best way to "handle" this problem but rather the best way to handle this is never to allow the list to be in a null state in the first place as also mentioned in the comments.

Have an empty list as the default value, this will save you quite a huge amount of if checks around your codebase depending on how many times getUsers() is being used and most importantly you wouldn't have to worry about NullPointerExeception's because they should never occur.

On another note, whenever you seem to see your self calling stream() on some collection and then immediately calling forEach you should realise it's wrong; 1) in the sense that you could easily call forEach directly on the list i.e. studentsByCourseRoster.getUsers().forEach(...) 2) streams and side-effect just don't work together well.

You can also filter null objects by filter(Objects::nonNull):

studentsByCourseRoster.getUsers().stream().filter(Objects::nonNull).forEach(user -> {
    final UserDTOv2 userDTO = new UserDTOv2();
    userDTO.populateUserDataFromUserDTO(user, groupedUsers);
    users.add(userDTO);
});

One more way to write this with optional, in addition to correct @Murenik's answer. No need to create empty list, we can pass execution non null case only with:

    Optional.ofNullable(nullableUsers)
            .ifPresent(users -> users.forEach(user -> {
                // work with user
            }));

Nullable optional is also works very well with nested null references, e.g. we have some structure:

class User {
    @Getter
    Address address;
}

class Address {
    @Getter
    String street;
}

then instead of writing

if (user.getAddress() != null && user.getAddress().getStreet() != null) {
   // work with street
}

we can use Optional:

    Optional.ofNullable(user)
            .map(User::getAddress)
            .map(Address::getStreet)
            .ifPresent(street -> {
                // work with street
            });
Related