1

I have the following code which works on two values viz. a List of String and a String. Both the List and String can be null.

List<String> myStringList = getMyStringList(); // may return null
String myString = "abc"; // may be null

List<String> finalStringList = Optional.ofNullable(myStringList)
            .map(strings -> strings.stream()
                    .filter(obj -> StringUtils.isEmpty(myString)
                            || myString.equals(obj))
                    .collect(Collectors.toList()))
            .orElseGet(ArrayList::new);

Idea is: If myStringList is null, then result in an empty ArrayList. If myString is null, then return myStringList as is. If myString is not null, then loop through the list and return all items matching myString.

Problem is, with solution above, I still loop through the whole list (strings.stream) even when myString is null.

Assuming myStringList is very big, how can I avoid looping the list for the case when myString is null?

That is,

1) do a null check on the list. If null, return a new empty list.

2) do a null check on myString. If null, return the list as is WITHOUT LOOPING.

3) if myString is not null, loop through the list and return the filtered list.

4
  • 2
    why you are not using simple if check. Commented Nov 6, 2019 at 5:09
  • 1
    Best solution: fix getMyStringList() to return an empty list instead of null. Commented Nov 6, 2019 at 5:11
  • 1
    Related and a very good read: How to best create a Java 8 stream from a nullable object? (I suggest that this question is partially a duplicate of that one) Commented Nov 6, 2019 at 5:18
  • @Vicky The question would be really clear if you could draw the expectation with all 4 possible combinations of myString and myStringList 's presence and absence. Commented Nov 6, 2019 at 9:19

4 Answers 4

2

You could use a ternary like,

List<String> finalStringList = myString == null ?
        Collections.emptyList()
        : Optional.ofNullable(myStringList)
        .map(strings -> strings.stream()
                .filter(obj -> StringUtils.isEmpty(myString)
                        || myString.equals(obj))
                .collect(Collectors.toList()))
        .orElseGet(ArrayList::new);

Or a basic if and default to an empty list. Like,

List<String> finalStringList = Collections.emptyList();
if (myString != null) {
    finalStringList = Optional.ofNullable(myStringList)
            .map(strings -> strings.stream()
                    .filter(obj -> StringUtils.isEmpty(myString)
                            || myString.equals(obj))
                    .collect(Collectors.toList()))
            .orElseGet(ArrayList::new);
}
Sign up to request clarification or add additional context in comments.

3 Comments

So you reckon I will have to use a ternary.. I thought of that.. but wanted to be sure if that is the only way and it can not be done using Streams and Optional..
The if does not use a ternary; so you don't "have to use a ternary". But you can.
As this requirement says: "2) do a null check on myString. If null, return the list as is WITHOUT LOOPING." than this Collections.emptyList() should be rather myStringList
1

This may work for you (with Java 9+):

List<String> result2 = Optional.ofNullable(myString)
  .filter(Objects::nonNull)
  .map(mystr -> Optional.ofNullable(myStringList)
                        .filter(Objects::nonNull)
                        .or(() -> Optional.of(new ArrayList<>()))
                        .stream()
                        .flatMap(List::stream)
                        .filter(mystr::equals)
                        .collect(Collectors.toList())) //ArrayList not guaranteed
    .orElse(myStringList);

If you're using Java 8, the following should work, but it looks a little harder to read:

List<String> result = Optional.ofNullable(myString)
  .filter(Objects::nonNull)
  .map(mystr -> Optional.ofNullable(myStringList)
                .filter(Objects::nonNull)
                .map(list -> list.stream()
                            .filter(mystr::equals)
                            .collect(Collectors.toList()))
                .orElseGet(ArrayList::new))
  .orElse(myStringList);

7 Comments

I am on Java 8. But your solution with Java 8 is not working.
It throws NullPointerException when myStringList is null.
@Vicky. No, I get an empty list when I test it. You may need to share your snippet.
added snippet in the answer
@Vicky Your requirements stated * If myString is null, then return myStringList as is*. In your example, myString is null, and therefore the result is null. But you're calling System.out.println(result.size()); without checking if result is null. That explains your NPE. Right?
|
0

Other solution

List<String> myStringList = Arrays.asList("abc", "aef"); // may return null
String myString = "abc"; // may be null

Map<Predicate, Supplier<List<String>>> predicateMap = Map.of(
      (v) -> myStringList == null, () -> new ArrayList<>(), // do a null check on the list. If null, return a new empty list.
      (v) -> myString == null, () -> myStringList, // do a null check on myString. If null, return the list as is WITHOUT LOOPING.
      (v) -> true, () -> myStringList.stream() // if myString is not null, loop through the list and return the filtered list.
                .filter(myString::equals)
                .collect(Collectors.toList())
      );
List<String> outputList = predicateMap.entrySet().stream()
      .filter(p -> p.getKey().test(null))
      .findFirst().get().getValue().get();

System.out.println(outputList);

Comments

0

Personally, I think the code by if-else will be much more readable/maintainable/efficient:

if (myStringList == null || myStringList.size() == 0) {
    return Collections.emptyList();
} else if (StringUtils.isEmpty(myString)) {
    return new ArrayList<>(myStringList); // or return myStringList;
} else {
    return myStringList.stream().filter(e -> myString.equals(e))
                                .collect(Collectors.toList());
}

Okay, if you really want to avoid writing a few lines of code, try my library: abacus-common

return N.isNullOrEmpty(myString) ? N.newArrayList(myStringList) 
                                 : N.filter(myStringList, e -> myString.equals(e));

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.