As I experimented Optional<T> does not handle null elements, so in the following example it throws a NullPointerException in the last statement:
List<String> data = Arrays.asList("Foo", null, "Bar");
data.stream().findFirst().ifPresent(System.out::println);
data.stream().skip(1).findFirst().ifPresent(System.out::println);
So, I still have to explicitly deal with null and filter non-null elements, such as:
data.stream()
.filter(item -> item != null)
.skip(1)
.findFirst()
.ifPresent(System.out::println);
Is there any alternative that avoids dealing explicitly with null as: item != null
You can use .filter(Objects::nonNull) using the method Objects.nonNull(…) added for this purpose.
There is no way to avoid explicit filtering, unless you avoid having nulls in your source list in the first place.
Note that it would be strange if the Optional handled the null in this case as it would yield an empty optional which had the semantic of “there is no first element” which implies “the stream was empty” which is just wrong.
Dealing with nulls explicitly is the cleanest solution here as it also allows you to explicitly tell whether you want .filter(Objects::nonNull).skip(1), .skip(1).filter(Objects::nonNull)…
…or .map(s->s==null? "null-replacement": s).findFirst()
It really depends on what you want to do. If you want to treat null as a valid value, the answer is different than if you want to skip nulls.
If you want to keep "nulls" in your stream:
List<String> data = Arrays.asList("Foo", null, "Bar");
data.stream().map(Optional::ofNullable).findFirst().flatMap(Function.identity()).ifPresent(System.out::println); ;
data.stream().map(Optional::ofNullable).skip(1).findFirst().flatMap(Function.identity()).ifPresent(System.out::println);
If you want to remove nulls from your stream, use data.stream().filter(Objects::nonNull) to filter it out (or as you stated o -> o != null, whatever you prefer.
Related
I would like to use Optionals with forEach in my example below, and am not sure about the correct approach.
Basically the functionality is as follows:
List<Long> myList;
List<Long> myResultList;
myList = getValues_A();
if (null != myList && !myList.isEmpty())
return;
for (Long singleVal : myList) {
List<Long> tempList = getValues_B(singleVal);
if (null != tempList && !tempList.isEmpty())
myResultList.addAll(tempList);
}
So I simple retrieve some data into myList, check if there is some value returned, and if so, I use the result to again retrieve data and put it in a final result list.
My idea with Optionals:
List<Long> myList;
List<Long> myResultList;
myList = getValues_A();
if (null != myList && !myList.isEmpty())
return;
myResult.forEach(itemToCheck -> Optional
.ofNullable(getValues_B(itemToCheck))
.ifPresent(myResultList::addAll));
Questions:
The first part:
myList = getValues_A();
if (null != myList && !myList.isEmpty())
return;
Is there any way to use Java 8 Optionals instead?
I.e.
myList = getValues_A();
if (!Optional.ofNullable(myList).isPresent())
return;
But this would only check for null and not if the object was empty (for which I also want to return). Can this be extended with a size check of the object within the Stream?
Also, misusing Optional's isPresent as a nullcheck only is bad coding practise I guess. Any other ideas?
The second part:
I assume that even empty objects will be attempted to be added to myResultList? Can this be somehow prevented in a similar approach, i.e. check if size = 0 within the stream?
myResult.forEach(itemToCheck -> Optional
.ofNullable(getValues_B(itemToCheck))
.ifPresent(myResultList::addAll));
Small sidenote: I can't use isEmpty(Object object) of org.apache.commons.lang3.ObjectUtils as I'm with version < 3.9.
I also think it is worth mentioning that besides whole reusing Optional is not good thing in any possible case(with which I agree). We also see in this approach that we create empty list and then altering its state by adding new elements. I thing if we can we should always avoid such solutions. Much cleaner approach is to instantiate list with its elements while declaring.
For getting rid of first part, you can make the getValues_A() function to return an Optional or an empty list instead of null.It make no sense to make any processing with Optional in this method.
Second part written with stream :
List<Long> myResultList = myList.stream().map(singleVal -> getValues_B(singleVal)).filter(Objects::nonNull).flatMap(List::stream).collect(Collectors.toList());
Each steps explained:
1. map(singleVal -> getValues_B(singleVal)) - each element of the list will be processed and you'll get a List as result for each.
2. filter(Objects::nonNull) - remove empty lists
3. flatMap(List::stream) - from stream of List<Long>,you'll obtain a stream of Long
4. collect(Collectors.toList()) - collect all resultList.
You may take advantage of the orELseGet() API of the Optional and the map/flatmap APIs of the stream to simplify your code.
List<Long> resultList = Optional.ofNullable(getValues_A())
.orElseGet(Collections::emptyList)
.stream()
.filter(Objects::nonNull)
.flatMap(l -> Optional.ofNullable(getValues_B(l))
.orElseGet(Collections::emptyList)
.stream()
.filter(Objects::nonNull))
.collect(Collectors.toList());
I have an EdmPortfolio type that has id (int), name (String) and tags which is a map as its members.
EdmPortfolio say has an id 1, and name as Portfolio1 and Map could have the following values,
analyst, John
region, US
I have a List, At the end i want to have a Map which has all the map values combined from all portfolios. Another EdmPortfolio might have the id as 2, name as Portfolio2 and Map with values analyst, Smith
region , UK
I want to have a combined Map with Values
region 'US','UK'
analyst 'John','Smith'
analyst and region are the keys to the map.
It has both the maps combined. I have the following code but i am a little lost
List<Map<Tag,String>> portfolioTagsList = new ArrayList<>();
for (EdmPortfolio edmPortfolio : edmPortfolioList) {
Map<Tag,String> portfolioTags = edmPortfolio.getPortfolioTags().entrySet()
.stream()
.filter(e -> (e.getValue() != ""|| e.getValue() !=null))
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue
));
portfolioTagsList.add(portfolioTags);
}
Map<Tag,String> finalTags = portfolioTagsList.stream()
.flatMap(m -> m.entrySet().stream())
.collect(Collectors.groupingBy(Map.Entry::getKey, Collectors.joining(",",Map.Entry::getValue)));
I am getting nonstatic method cannot be referenced from static context. How do i achieve this?
Thanks for your time
I'd do something like:
Map<Tag,String> finalTags = edmPortfolioList.stream()
.flatMap(edmPortfolio -> edmPortfolio.getPortfolioTags().entrySet().stream())
.filter(e -> (e.getValue() != "" || e.getValue() !=null)) // this seems weird, but just keeping
.collect(Collectors.groupingBy(e -> e.getKey(),
Collectors.mapping(ev -> ev.getValue(),
Collectors.joining(",")));
BTW: Any reason to break that in two streams?
Unfortunately, “nonstatic method cannot be referenced from static context” is the error that javac often ends up when type inference fails, especially in the context of method references or lambda expressions. The actual cause could be a wrong generic type parameter, a forgotten import statement, the use of a lambda expression or method reference at an inappropriate place, or even a misplaced or forgotten brace somewhere.
In your case, it’s Collectors.joining(",",Map.Entry::getValue) as that collector has no parameter of a function type that could accept a method reference.
You should use Collectors.mapping(Map.Entry::getValue, Collectors.joining(",")) instead.
Besides that, follow BrunoJCM’s suggestion to use a single stream operation and pay attention to the filter condition. e.getValue() != "" || e.getValue() !=null makes no sense as a reference can never be both, a reference to "" and the null reference, hence, this condition is always fulfilled. You most likely wanted to express that the string must not be null and not an empty string. In this regard, you should not compare strings by reference but use equals. For testing for an empty string, there is the alternative to use isEmpty() instead of equals(""). So the condition should be e.getValue()!=null && !e.getValue().isEmpty(). Mind the order, as we can invoke isEmpty() only after having verified that it is not null.
I want to have a null-safe comparator, but it does not work:
Comparator<Item> sort_high = (i1, i2)-> Double.compare(i2.getUser().getValue(), i1.getUser().getValue());
items.sort(Comparator.nullsFirst(sort_high));
However, I get a NPE, if item.getUser().getValue() (or item.getUser()) is null.
at Item.lambda$1(Item.java:270)
at java.util.Comparators$NullComparator.compare(Comparators.java:83)
at java.util.TimSort.countRunAndMakeAscending(TimSort.java:355)
at java.util.TimSort.sort(TimSort.java:220)
at java.util.Arrays.sort(Arrays.java:1438)
at java.util.List.sort(List.java:478)
What is wrong?
nullsFirst will take care of null Items ("i1"), but once two valid objects are found, your Comparator is invoked and you need to handle internal null references.
In your case, you could use something like:
items.sort(
Comparator.nullsFirst(
Comparator.comparing(Item::getUser,
Comparator.nullsFirst(Comparator.comparingDouble(User::getValue))
)
)
);
(Assuming getValue() returns a double)
But I hardly recommend such convoluted code.
Comparator.nullsFirst() doesn't mean that any NullPointerException thrown during the compare() method will be caught.
It says rather that a null compared object with always be less than a non null compared object and that if both are null, they are considered equal.
<T> Comparator<T> java.util.Comparator.nullsFirst(Comparator<? super
T> comparator)
Returns a null-friendly comparator that considers null to be less than
non-null. When both are null, they are considered equal. If both are
non-null, the specified Comparator is used to determine the order. If
the specified comparator is null, then the returned comparator
considers all non-null values to be equal.
In your case :
Comparator<Item> sort_high = (i1, i2)-> Double.compare(i2.getUser().getValue(), i1.getUser().getValue());
Compared objects are not null, that is i1 or i2 but fields of them are: i1.getUser() or i2.getUser().
You don't have a good use case for using directly Comparator.nullsFirst().
As workaround, you could use it to compare the User field in your compare() implementation as soon as one of two compared objects present a null User field.
I think that it is a good trade off between handling yourself null case and readability of the code :
private Comparator<User> comparatorUserNullFirst = Comparator.nullsFirst(Comparator.comparingDouble(User::getValue));
Comparator<Item> sortHigh = (i1, i2) -> {
if (i1.getUser() == null || i2.getUser() == null) {
return comparatorUserNullFirst.compare(i1.getUser(), i2.getUser());
}
return Double.compare(i2.getUser().getValue(), i1.getUser().getValue());
};
Another possibility, that is slightly more readable but does not distinguish between an item being null and an item having a user that is null, is the following:
Comparator<Item> sortLow = Comparator.comparing(item -> Optional.ofNullable(item)
.map(Item::getUser)
.map(User::getValue)
.orElse(null),
Comparator.nullsFirst(Comparator.naturalOrder())
);
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 :)
Unsurprisingly, following code will throw an ArgumentNullException
IEnumerable<string> collection = null;
string[] collectionViewAsAnArray = collection.ToArray();
This looks obvious at first sight ... but ain't incoherent to argue that returning null may have been a reasonnable alternative (Accounting that ToArray() is an extension method and therefor can be called, even on null).
While I acknowledge, this way, extension behave like a real method, I can't help finding the other approach really smart too ... but this may lead to other issues?
Granted IEnumerable is an interface used in collections but the underlying implementation is an object and you have set that collection variable to point to null, hence the exception.
If on the other hand you initialized collection with:
IEnumerable<string> collection = Enumerable.Empty<string>();
OR
IEnumerable<string> collection = new List<string>();
You would have an empty list object that you could act on. The ArgumentNullException exception is thrown because the collection argument is in fact null and that is what ToArray() is trying to act on. So logically, to me anyways, this was the only design choice.
Edit
On the other hand, in practice, I've made the conscious decision to always return a valid IEnumerable<T> when the return type on my class methods are supposed to return an IEnumerable<T>.
For example; a method signature that looks like IEnumerable<T> GetAll() would always return a valid enumerable and If there was nothing to return then I would return return Enumerable.Empty<T>();
The difference here to me is that GetAll() is not acting on a collection argument. You could really look at this like the collection is really nothing more than a parameter to the method and if you passed in a null parameter to a regular method you would probably throw an ArgumentNullException.
The short answer is that your collection variable is an expected argument (or parameter) to the ToArray() method and it is null so it makes sense to throw an ArgumentNullException.
Why push the error to some other spot in your code? If you expect that something can be null, check it before you use it. Otherwise throw an error exactly at the spot where your assumption was false, namely when you tried to convert it to an array.
I understand it can be debatable when you're chaining operations but in those cases I've found it easier to work with an empty IEnumerable rather than a null one.