Sense of Optional.orElse() [duplicate] - java-8

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 :)

Related

Why cant nullable arrays/hashmaps be accessed using []

When I have a nullable array/list/hashmap such as
var x: ArrayList<String>? = null
I know can access the element at index 1 like so
var element = x?.get(1)
or I could do it in a unsafe way like this
var element = x!![1]
but why can't I do something like this
var element = x?[1]
what's the difference between getting elements from an array using the first example and the last example, and why is the last example not allowed?
In the first example, you're using the safe call operator ?. to call the get function with it.
In the second example, you're using the [] operator on the non-nullable return value of the x!! expression, which of course is allowed.
However, the language simply doesn't have a ?[] operator, which would be the combination of the two. The other operators offered are also don't have null-safe variants: there's no ?+ or ?&& or anything like that. This is just a design decision by the language creators. (The full list of available operators is here).
If you want to use operators, you need to call them on non-nullable expressions - only functions get the convenience of the safe call operator.
You could also define your own operator as an extension of the nullable type:
operator fun <T> List<T>?.get(index: Int) = this?.get(index)
val x: ArrayList<String>? = null
val second = x[2] // null
This would get you a neater syntax, but it hides the underlying null handling, and might confuse people who don't expect this custom extension on collections.

Stream and Filter operations on a Map

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.

Optional<T> does not handle null elements

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.

What's the difference when assigning a value to a field in square brackets LINQ?

I have seen when coding in LINQ that when a value is assigned to a field sometimes is in this way Table["Field"] and any others like this Table.Field but can somebody explain me what's the difference please?
For example when modifying a field:
var ttAbccode_xRow =
(from ttAbccode_Row in ds.ABCCode select ttAbccode_Row).FirstOrDefault();
if (ttAbccode_xRow != null) {
ttAbccode_xRow["PI"] = 3.1416;
}
or
if (ttAbccode_xRow != null) {
ttAbccode_xRow.PI = 3.1416;
}
Accessing field via indexer (square brackets) returns object data type. That means that your compiler cannot detect data types incompatibility. You could assing for example string value (eg. "abcd") and you won't get error at design time, but as late as at runtime.
Second method (if available in your result set) is much more safe. Your property will have proper data type hence compiler will detect data types incompatibility at design time.
If I had both access methods available I would always prefer second one. It is less error prone.

Design choice: Any good reason for 'ToArray()' LINQ extension to throw an exception for null collections?

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.

Resources