16

Let's consider a Parent class which contains only one Integer attribute. I created 6 objects of parent class and with one null variable. Then I added these objects to a list.

I want to retrieve the corresponding object by the value of Integer attribute. I used Java 8 Streams for it.

Predicate<Parent> predicate = e -> e.getId() == 100; // sample attribute value
result = list.stream().filter(predicate).collect(Collectors.toList());

But I got NullPointerException, so I edited the code:

list.stream().filter(h -> h!=null).filter(predicate).collect(Collectors.toList());

But I want to throw an exception if any of the object is null. If no objects in the list is null, then I want to retrieve the corresponding object from list.

How can I achieve this using a single statement using Java 8 Streams?

9
  • 1
    Your first answer was doing exactly this only right? Why did you filter out the null values then?
    – Codebender
    Commented Jul 10, 2015 at 6:57
  • 1
    Why not simply catch the NPE, wrap it in your own exception, and throw that exception on?
    – Buurman
    Commented Jul 10, 2015 at 7:07
  • 1
    @Buurman: It is actually pretty dangerous to catch a NPE: What if there is a programming error in the code and there is a NPE for some other reason? Then it will get mistaken for an absent Parent in the list.
    – Lii
    Commented Jul 10, 2015 at 7:43
  • 1
    @Lii: that’s easy to check: catch(NullPointerException ex){ if(list.contains(null)) throw new CustomException(); else throw ex; }, that way other NPEs are retained. The check has some costs but that doesn’t matter as it is only performed in the erroneous case.
    – Holger
    Commented Jul 10, 2015 at 8:48
  • 1
    @Holger: Hm... I don't really think it's a good practice. There is a slight chance that there is an unexpected null dereference that is triggered when the list also contains null. I would stick to the rule of always avoiding to catch NPEs. It is too easy to get wrong since they can be thrown basically anywhere. Tricky example: If e.getId() happen to return a null Integer.
    – Lii
    Commented Jul 10, 2015 at 8:58

4 Answers 4

23

JB Nizet answer is okay, but it uses map only for its side effects and not for the mapping operation, which is kind of weird. There is a method which can be used when you are solely interested in the side effects of something, such as throwing an exception: peek.

List<Parent> filtered = list.stream()
    .peek(Objects::requireNonNull)
    .filter(predicate)
    .collect(Collectors.toList());

And if you want your own exception just put a lambda in there:

List<Parent> filtered = list.stream()
    .peek(p -> { if (p == null) throw new MyException(); })
    .filter(predicate)
    .collect(Collectors.toList());

Note about non exhausted streams

Note that, regardless of if you use map or peek, you have to make sure that the stream is consumed in its entirety for all the elements to be checked! Otherwise the exception might not be thrown even if there are null elements.

Examples where all elements might not be checked:

  • limit is used.
  • allMatch is used.
  • The stream is filtered BEFORE the null check pipeline stage.

Checked Exceptions

If your exception is checked you can either check for null beforehand, if you don't mind traversing the list twice. This is probably best in your case, but might not always be possible.

if (list.contains(null)) throw new MyCheckedException();

You could also throw an unchecked exception in your stream pipeline, catch it and then throw the checked one:

try {
    ...
        .peek(p -> { if (p == null) throw new MyException(); })
    ...
} catch (MyException exc) {
    throw new MyCheckedException();
}
12
  • 1
    @Codebender: That's right. If it must be checked then a sneaky throw method might be warranted.
    – Lii
    Commented Jul 10, 2015 at 7:37
  • 2
    The sneaky throw doesn’t work the way you have written. In your code, the compiler will perfectly infer the right checked exception and complain. Besides that, peek is meant to support debugging but not to be part of production code logic.
    – Holger
    Commented Jul 10, 2015 at 9:12
  • 3
    @Holger, to my opinion using peek in production code might be ok if you setup the processed element for further operation and have no other side-effects like names.stream().map(Node::new).peek(n -> n.setParent(this)).forEach(this::addNode) instead of longer names.stream().map(name -> {Node n = new Node(name); n.setParent(this); return n;}).forEach(this::addNode). Commented Jul 10, 2015 at 10:02
  • 2
    @Holger "The sneaky throw doesn’t work": You're right, I made a mistake I've updated. "Besides that, peek is meant to support debugging": The documentation says "This method exists mainly to support debugging". If there are no practical problem with it I think it is okay.
    – Lii
    Commented Jul 10, 2015 at 10:56
  • 1
    @Tagir Valeev: of course, using map for executing a Consumer is not better. But why comparing two wrong approaches anyway? In your example, the correct solution would be names.stream().map(Node::new).forEach(n -> {n.setParent(this); addNode(n);})
    – Holger
    Commented Jul 10, 2015 at 11:48
4

Let’s start with the simplest solution:

if(list.contains(null)) throw new MyException();
result = list.stream().filter(predicate).collect(Collectors.toList());

If you suspect the list to contain nulls and even have a specialized exception type to flag this condition, a pre-check is the cleanest solution. This ensures that such condition doesn’t silently remain if the predicate changes to something that can handle nulls or when you use a short-circuiting stream operation that may end before encountering a subsequent null.

If the occurrence of null in the list still is considered a programming error that shouldn’t happen, but you just want to change the exception type (I can’t imagine a real reason for this), you may just catch and translate the exception:

try {
    result = list.stream().filter(predicate).collect(Collectors.toList());
}
catch(NullPointerException ex) {
    if(list.contains(null)) // ensure that we don’t hide another programming error
        throw new MyException();
    else throw ex;
}

This works efficient under the assumption that null references do not occur. As said, if you suspect the list to contain null you should prefer a pre-check.

6
  • 1
    I'll add my comment about catching NPEs here also: I don't really think it's a good practice. I would stick to the rule of always avoiding to catch NPEs. It is too easy to get wrong since they can be thrown basically anywhere. In this example there is a slight chance that there is an unexpected null dereference that is triggered when the list also contains null. Tricky example: If e.getId() happens to return a null Integer.
    – Lii
    Commented Jul 10, 2015 at 9:13
  • 2
    @Lii: that isn’t an issue since, if you have two programming errors, it isn’t really important which one gets flagged. As I already said in the answer, this solution is only for use cases, where the other exception type has similar semantics (programming error) as the NPE and that I therefore don’t see a real value in changing the exception type. And I don’t know whether this is really worse than abusing peek or using sneaky throwing technique…
    – Holger
    Commented Jul 10, 2015 at 9:15
  • 1
    That's why I threw another exception class in my solution which surrounded the stream operations with a try-catch.
    – Lii
    Commented Jul 10, 2015 at 9:16
  • 1
    But having a null element in the list is likely (or at least not necessarily) not a programming error. Just an exceptional condition which should be reported by an exception.
    – Lii
    Commented Jul 10, 2015 at 9:17
  • 2
    See the first part of my answer. If having a null element in the list is likely not a programming error, a pre-check should be used. I clearly said in the answer that the second part only applies “if the occurrence of null in the list still is considered a programming error” (despite the exception type translation).
    – Holger
    Commented Jul 10, 2015 at 9:19
2

::map vs ::peek

Use Stream#map and NEVER USE Stream#peek method in production code. As Javadoc says peek was mainly to support debugging ...where the stream implementation is able to optimize away..., the action will not be invoked for those elements. REF: javadoc

java.utils.Objects#requireNonNull

  list.stream()
      .map(Objects::requireNonNull)
      .filter(predicate)

This code clearly separates the validation part and filter part. It will contain "requireNonNull" frame in the exception call stack. Although, by java.util design, it'll rise NPE exceptoin type. REF: javadoc. SRC: openjdk


requireNonNull with a static message

  list.stream()
      .map(item -> requireNonNull(item, "Null item in the list."))
      .filter(predicate)

import static java.util.Objects.requireNonNull;

REF: javadoc. SRC: openjdk


requireNonNull with a custom message supplier

  list.stream()
      .map(item -> requireNonNull(item, () -> "Expected non-null items. Was: " + list))
      .filter(predicate)

REF: javadoc. SRC: openjdk


custom validator

  list.stream()
      .map(this::requireItemNonNull)
      .filter(predicate)
      ...


private Parent requireItemNonNull(Parent item) {
  if (null == item) throw new IllegalStateException("Expected: non null items. Was: " + this.list);
  return item;

validate all

If you only need to validate all items, use a collector at the end. E.g. toArray as stream postpones actual iteration until the "collect" time:

  list.stream()
      .map(this::validateItemNonNull)
      .toArray();
2
  • 1
    The Javadoc of peek uses the phrase "exists mainly to support debugging" because the method is mainly useful for that, not because it is forbidden to use it for anything else. Nowhere does it say that you must "never" use it for production code. It is fine to use it in production code in the right situations (although those are rare).
    – Lii
    Commented Feb 16, 2023 at 8:48
  • 1
    Your solution with map has exactly the same problems a the Javadoc of peek describes: The operation might not be invoked for all elements if the stream is not fully consumed. On top of that the solution with map has yet another problem: It uses map only for side effects, without performing any actual mapping, which is surprising and weird. peek is the perfect choice for this situation.
    – Lii
    Commented Feb 16, 2023 at 8:49
1
Function<Parent, Parent> throwingIdentity = p -> {
    if (p == null) {
        throw new MyException();
    }
    return p;
};

List<Parent> filtered = 
    list.stream()
        .map(throwingIdentity)
        .filter(predicate)
        .collect(Collectors.toList());

Not the answer you're looking for? Browse other questions tagged or ask your own question.