I know the subject may be a bit in advance as the JDK8 is not yet released (and not for now anyway..) but I was reading some articles about the Lambda expressions and particularly the part related to the new collection API known as Stream.
Here is the example as given in the Java Magazine article (it is an otter population algorithm..):
Set<Otter> otters = getOtters();
System.out.println(otters.stream()
.filter(o -> !o.isWild())
.map(o -> o.getKeeper())
.filter(k -> k.isFemale())
.into(new ArrayList<>())
.size());
My question is what happen if in the middle of the Set internal iteration, one of the otter is null?
I would expect a NullPointerException to be thrown but maybe am I still stuck in the previous development paradigm (non-functional), can someone enlighten me as how this should be handled?
If this really throw a NullPointerException, I find the feature quite dangerous and will have to be used only as below:
Developer to ensure there is no null value (maybe using a previous
.filter(o -> o != null))
Developer to ensure the application is never
generating null otter or a special NullOtter object to deal with.
What is the best option, or any other option?
Although the answers are 100% correct, a small suggestion to improve null case handling of the list itself with Optional:
List<String> listOfStuffFiltered = Optional.ofNullable(listOfStuff)
.orElseGet(Collections::emptyList)
.stream()
.filter(Objects::nonNull)
.collect(Collectors.toList());
The part Optional.ofNullable(listOfStuff).orElseGet(Collections::emptyList) will allow you to handle nicely the case when listOfStuff is null and return an emptyList instead of failing with NullPointerException.
Stuart's answer provides a great explanation, but I'd like to provide another example.
I ran into this issue when attempting to perform a reduce on a Stream containing null values (actually it was LongStream.average(), which is a type of reduction). Since average() returns OptionalDouble, I assumed the Stream could contain nulls but instead a NullPointerException was thrown. This is due to Stuart's explanation of null v. empty.
So, as the OP suggests, I added a filter like so:
list.stream()
.filter(o -> o != null)
.reduce(..);
Or as tangens pointed out below, use the predicate provided by the Java API:
list.stream()
.filter(Objects::nonNull)
.reduce(..);
From the mailing list discussion Stuart linked:
Brian Goetz on nulls in Streams
Current thinking seems to be to "tolerate" nulls, that is, to allow them in general, although some operations are less tolerant and may end up throwing NPE. See the discussion of nulls on the Lambda Libraries expert group mailing list, specifically this message. Consensus around option #3 subsequently emerged (with a notable objection from Doug Lea). So yes, the OP's concern about pipelines blowing up with NPE is valid.
It's not for nothing that Tony Hoare referred to nulls as the "Billion Dollar Mistake." Dealing with nulls is a real pain. Even with classic collections (without considering lambdas or streams) nulls are problematic. As fge mentioned in a comment, some collections allow nulls and others do not. With collections that allow nulls, this introduces ambiguities into the API. For example, with Map.get(), a null return indicates either that the key is present and its value is null, or that the key is absent. One has to do extra work to disambiguate these cases.
The usual use for null is to denote the absence of a value. The approach for dealing with this proposed for Java SE 8 is to introduce a new java.util.Optional type, which encapsulates the presence/absence of a value, along with behaviors of supplying a default value, or throwing an exception, or calling a function, etc. if the value is absent. Optional is used only by new APIs, though, everything else in the system still has to put up with the possibility of nulls.
My advice is to avoid actual null references to the greatest extent possible. It's hard to see from the example given how there could be a "null" Otter. But if one were necessary, the OP's suggestions of filtering out null values, or mapping them to a sentinel object (the Null Object Pattern) are fine approaches.
If you just want to filter null values out of a stream, you can simply use a method reference to java.util.Objects.nonNull(Object). From its documentation:
This method exists to be used as a Predicate, filter(Objects::nonNull)
For example:
List<String> list = Arrays.asList( null, "Foo", null, "Bar", null, null);
list.stream()
.filter( Objects::nonNull ) // <-- Filter out null values
.forEach( System.out::println );
This will print:
Foo
Bar
An example how to avoid null e.g. use filter before groupingBy
Filter out the null instances before groupingBy.
Here is an example
MyObjectlist.stream()
.filter(p -> p.getSomeInstance() != null)
.collect(Collectors.groupingBy(MyObject::getSomeInstance));
If you do not want to iterate two times (filter + map or any). Try this.
private static void trimAll() {
String[] emtLne = {"", " ", " cc ", null, " xx "};
System.out.println(Arrays.stream(emtLne).map(val -> (val != null) ? val.trim() : "").collect(Collectors.joining()));
}
Output
ccxx
Related
Find the object matching with a Property value from a Collection using Java 8 Stream.
List<Person> objects = new ArrayList<>();
Person attributes -> Name, Phone, Email.
Iterate through list of Persons and find object matching email.
Saw that this can be done through Java 8 stream easily. But that will still return a collection?
Ex:
List<Person> matchingObjects = objects.stream.
filter(p -> p.email().equals("testemail")).
collect(Collectors.toList());
But I know that it will always have one unique object. Can we do something instead of Collectors.toList so that i got the actual object directly.Instead of getting the list of objects.
Instead of using a collector try using findFirst or findAny.
Optional<Person> matchingObject = objects.stream().
filter(p -> p.email().equals("testemail")).
findFirst();
This returns an Optional since the list might not contain that object.
If you're sure that the list always contains that person you can call:
Person person = matchingObject.get();
Be careful though! get throws NoSuchElementException if no value is present. Therefore it is strongly advised that you first ensure that the value is present (either with isPresent or better, use ifPresent, map, orElse or any of the other alternatives found in the Optional class).
If you're okay with a null reference if there is no such person, then:
Person person = matchingObject.orElse(null);
If possible, I would try to avoid going with the null reference route though. Other alternatives methods in the Optional class (ifPresent, map etc) can solve many use cases. Where I have found myself using orElse(null) is only when I have existing code that was designed to accept null references in some cases.
Optionals have other useful methods as well. Take a look at Optional javadoc.
findAny & orElse
By using findAny() and orElse():
Person matchingObject = objects.stream().
filter(p -> p.email().equals("testemail")).
findAny().orElse(null);
Stops looking after finding an occurrence.
findAny
Optional<T> findAny()
Returns an Optional describing some element of the stream, or an empty Optional if the stream is empty.
This is a short-circuiting terminal operation.
The behavior of this operation is explicitly nondeterministic; it is free to select any element in the stream. This is to allow for maximal performance in parallel operations; the cost is that multiple invocations on the same source may not return the same result. (If a stable result is desired, use findFirst() instead.)
Guava API provides MoreCollectors.onlyElement() which is a collector that takes a stream containing exactly one element and returns that element.
The returned collector throws an IllegalArgumentException if the stream consists of two or more elements, and a NoSuchElementException if the stream is empty.
Refer the below code for usage:
import static com.google.common.collect.MoreCollectors.onlyElement;
Person matchingPerson = objects.stream
.filter(p -> p.email().equals("testemail"))
.collect(onlyElement());
You can use the method .reduce() instead .get() or .orElseGet().
That will help you to avoid NoSuchElementException and NullPointerException
Person matchingObject = objects.stream()
.filter(p -> p.email().equals("testemail"))
.reduce(DefaultObjectReturned, (a1, resultAsPerson) -> resultAsPerson);
Faced to some interesting issue: for example, I have to choose some solutions, depending on whether my List<> is blank or not, but I don't want to use simple if-statement for refactoring reasons (for example, there are three ugly inner if-s). If I had a nullable object, I could use the Optional::ifPresentOrElse solution, but what if the array is not null but just empty? are there any built-in language solutions? I understand that I could write something like ListOptional, create my own ListOptional::isNotEmptyOrElse there, but perhaps there is another solution?
Understand Optional as a wrapper against a possibly null instance providing chainable null-safe methods. Suggested ListOptional doesn't make to me any sense as null and empty lists semantically represent the same - a bunch of nothing.
If you want to use Optional, you must filter the single list in it whether is empty or not and provide an alternative:
List<String> nonEmpty = Optional.of(list)
.filter(l -> !l.isEmpty()) // is the list empty?
.orElseGet(() -> Arrays.asList("Hi", "Bye")); // if so, yield a default one
How is this better than using a ternary operator is questionable. I see no advantage over using this or if-else construct or a wrapper from a 3rd party library:
if (list.isEmpty() {
list = Arrays.asList("Hi", "Bye");
}
A built-in language solution for handling potentially empty collections is Stream API as declarative calls of chained methods, that are not executed on Stream from an empty collection. You can understand Stream as a wrapper against a possibly empty collection with the goal of processing each element. The same goes to the Optional with a difference a single instance is handled (regardless of whether it is a collection or not).
List<String> list = Collections.emptyList(); // empty list
List<String> updated = list.stream()
.filter(str -> str.startsWith("A") // won't happen
.map(String::toLowerCase) // won't happen
.collect(Collectors.toList()); // yields empty list
I know I can filter a stream to get only those elements that are not null, and then do with them whatever I need. Something like this:
myList.stream().filter(element -> element != null).doOtherThings...
Is there a way to assert that the elements are not null in a stream function, so that it goes through all elements and if it finds one being null, it throws an exception? I've been thinking of something like the following:
myList.stream().assert(Objects::nonNull).doOtherThings...
Use something like
.map(Objects::requireNonNull)
You just need to apply an operation which will throw an exception, but otherwise passes the argument through.
Although, unless there is a good reason not to iterate the list multiple times, it might be clearer to separate the checking and the processing:
if (myList.stream().anyMatch(Objects::isNull)) {
throw ...
}
// Do things with list.
There are some very good suggestions already. Allow me to supplement. If what you are after is an assertion as in an assert statement, I would like to make this explicit in the code in order to guide the reader about my purpose. To assert that your original list doesn’t contain any nulls:
assert ! myList.contains(null);
If the assertion is to be checked somewhere down the stream pipeline, the simple way is:
assert myList.stream().map(this::transform).allMatch(Objects::nonNull);
If you don’t want to create a separate stream for the assertion but prefer to assert in the middle of your existing stream pipeline, use for example:
myList.stream()
.peek(e -> { assert e != null; })
.toArray();
You may worry that the use of peek is not so nice, which is also why I mention this option last. peek is documented to exist “mainly to support debugging” (quote taken out of its context), so you may say that it’s related to the purpose of assert and thus defend its use in this case.
To the map method I prefer to use peek method. I think it is more expressive then uing map method that has to return some value.
list.stream()
.peek(Objects::requireNonNull)
However what i think is hard to understand is that stream is not executed until the collect() is called. That is why doing this kind of conditional logic is hard using streams. To be honest if you want to achieve the desired effect you have to do something like this.
list.stream()
.peek(Objects::requireNonNull)
.collect(Collectors.toList())
.stream()
.map(t -> do whatever ... )
So I've created a stream which comes from a List but I'm having some trouble converting it into a <ObjectType>, here's what I've tried:
ObjectType sco = getList().stream()
.filter(p -> p.getValue() == value)
.findFirst(); //also tried .orElse(null);
would anyone, please, be so kind as to suggest where I'm going wrong?
(I apologize if this has been asked before but I couldn't find a discussion on this particular topic (or didn't know the proper terms to search for))
Thanks in advance!
findFirst() gives you an Optional and you then have to decide what to do if it's not present. So findFirst().orElse(null) should give you the object or null if it's not present
You could just do a .get() on the Optional, but that could be regarded as poor practice since get() will throw an exception if Optional has no content. You should normally assert presence/absence of the Optional and decide what to do in each case (that's why it's there - so that you know something is truly optional and you have to determine what to do)
If you have an action you want to perform on object presence, and you don't want to do anything on absence, you can call .ifPresent() and provide a lambda as an argument. That will be called with the contained object, if present.
As of Java 9, a further solution would be to use Optional.ifPresentOrElse()
I think you may be looking for findFirst().or Else(null). findFirst() will return an Optional - empty in the case of an empty steam.
Unless I misunderstood your comment. Have you tried this, or did you try orElse(null) without findFirst()?
Why does this throw a java.lang.NullPointerException?
List<String> strings = new ArrayList<>();
strings.add(null);
strings.add("test");
String firstString = strings.stream()
.findFirst() // Exception thrown here
.orElse("StringWhenListIsEmpty");
//.orElse(null); // Changing the `orElse()` to avoid ambiguity
The first item in strings is null, which is a perfectly acceptable value. Furthermore, findFirst() returns an Optional, which makes even more sense for findFirst() to be able to handle nulls.
EDIT: updated the orElse() to be less ambiguous.
The reason for this is the use of Optional<T> in the return. Optional is not allowed to contain null. Essentially, it offers no way of distinguishing situations "it's not there" and "it's there, but it is set to null".
That's why the documentation explicitly prohibits the situation when null is selected in findFirst():
Throws:
NullPointerException - if the element selected is null
As already discussed, the API designers do not assume that the developer wants to treat null values and absent values the same way.
If you still want to do that, you may do it explicitly by applying the sequence
.map(Optional::ofNullable).findFirst().flatMap(Function.identity())
to the stream. The result will be an empty optional in both cases, if there is no first element or if the first element is null. So in your case, you may use
String firstString = strings.stream()
.map(Optional::ofNullable).findFirst().flatMap(Function.identity())
.orElse(null);
to get a null value if the first element is either absent or null.
If you want to distinguish between these cases, you may simply omit the flatMap step:
Optional<String> firstString = strings.stream()
.map(Optional::ofNullable).findFirst().orElse(null);
System.out.println(firstString==null? "no such element":
firstString.orElse("first element is null"));
This is not much different to your updated question. You just have to replace "no such element" with "StringWhenListIsEmpty" and "first element is null" with null. But if you don’t like conditionals, you can achieve it also like:
String firstString = strings.stream()
.map(Optional::ofNullable).findFirst()
.orElseGet(()->Optional.of("StringWhenListIsEmpty"))
.orElse(null);
Now, firstString will be null if an element exists but is null and it will be "StringWhenListIsEmpty" when no element exists.
You can use java.util.Objects.nonNull to filter the list before find
something like
list.stream().filter(Objects::nonNull).findFirst();
The following code replaces findFirst() with limit(1) and replaces orElse() with reduce():
String firstString = strings.
stream().
limit(1).
reduce("StringWhenListIsEmpty", (first, second) -> second);
limit() allows only 1 element to reach reduce. The BinaryOperator passed to reduce returns that 1 element or else "StringWhenListIsEmpty" if no elements reach the reduce.
The beauty of this solution is that Optional isn't allocated and the BinaryOperator lambda isn't going to allocate anything.
Optional is supposed to be a "value" type. (read the fine print in javadoc:) JVM could even replace all Optional<Foo> with just Foo, removing all boxing and unboxing costs. A null Foo means an empty Optional<Foo>.
It is a possible design to allow Optional with null value, without adding a boolean flag - just add a sentinel object. (could even use this as sentinel; see Throwable.cause)
The decision that Optional cannot wrap null is not based on runtime cost. This was a hugely contended issue and you need to dig the mailing lists. The decision is not convincing to everybody.
In any case, since Optional cannot wrap null value, it pushes us in a corner in cases like findFirst. They must have reasoned that null values are very rare (it was even considered that Stream should bar null values), therefore it is more convenient to throw exception on null values instead of on empty streams.
A workaround is to box null, e.g.
class Box<T>
static Box<T> of(T value){ .. }
Optional<Box<String>> first = stream.map(Box::of).findFirst();
(They say the solution to every OOP problem is to introduce another type :)