Mazeryt
Mazeryt

Reputation: 915

Filtering List with nullable values

I trying to replace existing loop with java lambda expresions.

I have simple data structure that looks like follow:

class DbObject{
    private Long objectId;

    private Long basedOnObjectId; //<- this field can be null

    public Long getObjectId() {
        return objectId;
    }

    public void setObjectId(Long objectId) {
        this.objectId = objectId;
    }

    public Long getBasedOnObjectId() {
        return basedOnObjectId;
    }

    public void setBasedOnObjectId(Long basedOnObjectId) {
        this.basedOnObjectId = basedOnObjectId;
    }
}

Then I have a List of DbObjects

And I trying to filter each DbObject with specified basedOnObjectId:

(1) list.stream().filter(object -> obejct.getBasedOnObjectId()
             .equals(basedOnObject.getObjectId()))
             .collect(Collector.of(List))

Of course this code gives me NullPointer because some od DbObject didn't have BasedOnObjectId because they are roots.

So natural thing is to replace field "Long basedOnObjectId;" by Optional but as I mentioned at start there are existing production code co it isn't fast fix.

So I trying to make some changes, I evaluate following sentence:

 Optional<Long> option = Optional.ofNullable(objectWithNullBasedOnId.getBasedOnObjectId());

And Try with .get() method or .ifPresent() but .get() also throw null pointer exception and ifPresent is not dedicated to work with stream.

I noticed that and the fact that When I have DbObject with null basedOnObjectId I don't have to check if value is exactly null but only skip this step of execution.

So I decided to use .orElse() method and return some fake value. In this case i return 0L value because this index don't exist in DB :)

So my code :

 (2) List newList = list.stream().filter(object -> Optional.ofNullable(object.getBasedOnObjectId())
                             .orElse(0L)
                             .equals(basedOnObject.getObjectId()) )
                             .collect(Collectors.toList()) ;

And this work correctly in this case, but I think this is some kind of workaround. In other languages are possible to skip execution with null value, or use some like skip-skipWhen methods.

My question is : if exist possibility to reach the goal of (2) expression using dedicated method (in example Optional) from java 8 and write "in stream style" :)

Upvotes: 0

Views: 2424

Answers (3)

Brian Goetz
Brian Goetz

Reputation: 95346

You don't need to introduce Optional here. You just need to be aware that null is in your domain, and deal accordingly. Like this:

list.stream().filter(o -> Objects.equals(o.getBasedOnObjectId(), 
                                         basedOnObject.getObjectId()))
             .collect(toList());

or by filtering out the nulls explicitly (.filter(o -> o.getBasedOnObjectId() != null) or by rolling a null check into your existing filter condition.

No reason to make this more complicated than it needs to be. Objects.equals() is likely what you want, just use that.

Upvotes: 5

nosid
nosid

Reputation: 50044

If you compare Optional<Long> instead of Long, there is no need for a distinction between present and absent objects.

Optional<Long> root = Optional.of(basedOnObject.getObjectId());
list.stream()
    .map(DbObject::getBasedOnObjectId)
    .map(Optional::ofNullable)
    .filter(root::equals)
    .collect(Collectors.toList());

In this case, it is even simpler, because you can just reverse the comparison:

list.stream()
    .filter(o -> basedOnObject.getObjectId().equals(o.getBasedOnObjectId()))
    .collect(Collectors.toList());

Upvotes: 3

Matthew Franglen
Matthew Franglen

Reputation: 4532

You probably want to actually remove the null objects from the stream before passing them to subsequent operations. Try mapping the objects to the BasedOnObjectId form and then filtering the null values:

list.stream()
    .map(object -> object.getBasedOnObjectId())
    .filter(object -> object != null)
    ...

Upvotes: 3

Related Questions