Finding string in optional list of lists

Viewed 196

I have a list of 'Blocks' that could be null. Each of the blocks contains a list of names that also could be null. I'm trying to find if any of the names in all the blocks is the word "blurb".

I have the following code which does work:

private boolean containsText(List<Blocks> blocks) {
        return Optional.ofNullable(blocks)
            .map(Collection::stream)
            .map(o -> o.map(Blocks::getNames))
            .map(e -> e.anyMatch(names -> names != null && names.contains("blurb")))
            .orElse(false);
}

But since getNames could return null I have to check for it in the next statement. I could wrap it in another Optional at that point, but then I would end up with an Optional<Stream<Optional<List<String>>>>

Using names -> names != null seems cleaner? Or is there a way to simplify this?

3 Answers

With Java-9 you can use Stream.ofNullable as follows:

public boolean containsText(List<Blocks> blocks) {
    return Stream.ofNullable(blocks)
            .flatMap(b -> b.stream().map(Blocks::getNames).filter(Objects::nonNull))
            .anyMatch(s -> s.contains("blurb"));
}

Another simpler variant without the use of Optional would just be :

private boolean containsText(List<Blocks> blocks) {
    return blocks != null && blocks.stream()
            .map(Blocks::getNames)
            .filter(Objects::nonNull)
            .anyMatch(list -> list.contains("blurb"));
}

Note: One of your problems is the design where you say, "I have a list of 'Blocks' that could be null", you should fix it to return an empty list for such representation and then get rid of the null check in the above code.

I would not recommend using Optional for null check you can use ternary operator to check blocks is null and simply return false if it is null, else stream the blocks list and check for string

return Objects.isNull(blocks) ? false : blocks.stream()
                                             .map(Blocks::getNames)
                                             .filter(Objects::nonNull)
                                             .anyMatch(list->list.contains("blurb"));

You can also add simple null for existing code

private boolean containsText(List<Blocks> blocks) {
    return Optional.ofNullable(blocks)
        .map(Collection::stream)
        .map(o -> o.map(Blocks::getNames).filter(Objects::nonNull))
        .map(e -> e.anyMatch(names -> names.contains("blurb")))
        .orElse(false);
}
Related