If else code execution with Optional class - java

I was going through a tutorial of Optional class here - https://www.geeksforgeeks.org/java-8-optional-class/ which has the following
String[] words = new String[10];
Optional<String> checkNull = Optional.ofNullable(words[5]);
if (checkNull.isPresent()) {
String word = words[5].toLowerCase();
System.out.print(word);
} else{
System.out.println("word is null");
}
I am trying to make it of less lines using ifPresent check of Optional as
Optional.ofNullable(words[5]).ifPresent(a -> System.out.println(a.toLowerCase()))
but not able to get the else part further
Optional.ofNullable(words[5]).ifPresent(a -> System.out.println(a.toLowerCase())).orElse();// doesn't work```
Is there a way to do it?

Java-9
Java-9 introduced ifPresentOrElse for something similar in implementation. You could use it as :
Optional.ofNullable(words[5])
.map(String::toLowerCase) // mapped here itself
.ifPresentOrElse(System.out::println,
() -> System.out.println("word is null"));
Java-8
With Java-8, you shall include an intermediate Optional/String and use as :
Optional<String> optional = Optional.ofNullable(words[5])
.map(String::toLowerCase);
System.out.println(optional.isPresent() ? optional.get() : "word is null");
which can also be written as :
String value = Optional.ofNullable(words[5])
.map(String::toLowerCase)
.orElse("word is null");
System.out.println(value);
or if you don't want to store the value in a variable at all, use:
System.out.println(Optional.ofNullable(words[5])
.map(String::toLowerCase)
.orElse("word is null"));

For a bit to be more clear ifPresent will take Consumer as argument and return type is void, so you cannot perform any nested actions on this
public void ifPresent(Consumer<? super T> consumer)
If a value is present, invoke the specified consumer with the value, otherwise do nothing.
Parameters:
consumer - block to be executed if a value is present
Throws:
NullPointerException - if value is present and consumer is null
So instead of ifPreset() use map()
String result =Optional.ofNullable(words[5]).map(String::toLowerCase).orElse(null);
print Just to print
System.out.println(Optional.ofNullable(words[5]).map(String::toLowerCase).orElse(null));

If you are using java 9, you can use ifPresentOrElse() method::
https://docs.oracle.com/javase/9/docs/api/java/util/Optional.html#ifPresentOrElse-java.util.function.Consumer-java.lang.Runnable-
Optional.of(words[5]).ifPresentOrElse(
value -> System.out.println(a.toLowerCase()),
() -> System.out.println(null)
);
If Java 8 then look this great cheat sheet :
http://www.nurkiewicz.com/2013/08/optional-in-java-8-cheat-sheet.html

Related

Set value to a string variable, from an object based on the condition using Java Streams

I am new to Java streams. I have a code snippet that i need to write using java streams. I am trying to set a value to a string based on a condition. I tried to look for solutions and experimented by using anyMatch, however could not get anywhere.
String loadAGENTID = "";
for(ReportGenerationParameter rgp : loadReportTableExt.getReportGenerationParameters()) {
if (rgp.getKey().equalsIgnoreCase(RapFilter.IDUSR)) {
loadAGENT_ID = rgp.getValue();
}
}
String loadAGENTID is to be used in the code. Any suggestion is welcome. Thank you.
I have tried using Arrays.stream and anyMatch but no luck so far
boolean todoName =
Arrays.stream(loadReportTableExt.getReportGenerationParameters())
.anyMatch(item -> item.getKey().equalsIgnoreCase(RapFilter.IDUSR));
if (todoName) {
// want to set the value of the respective object.
loadAGENT_ID = item.getValue();
}
Use the filter to find the matching object and then use findFirst which returns the first matching element
String loadAGENTID = loadReportTableExt.getReportGenerationParameters()
.stream()
.filter(rgp-> rgp.getKey().equalsIgnoreCase(RapFilter.IDUSR))
.findFirst()
.map(rgp->rgp.getValue()) // returns value from first matching element
.orElse(""); // none of them matched returns default value

Java 8 stream through multiple layers and concatenate all items at the bottom-most layer

I currently have a multiple layer structure data that is like this:
Industry class has a private field Set<Company> that can be null.
Company class has a private field Set<Division> that can be null.
Division class has a private field Set<Group> that can be null.
Group class has a private field groupName that can be null and is
retrievable with a getter (getGroupName()).
I am trying to stream an instance of Industry all way down to the Group layer and concatenate all the groupName's into one String with "/" in between.
If the this instance of Industry doesn't contain any groupName, return the string "null".
Based on my limited knowledge of Java 8, I am thinking of coding like this:
industry.stream()
.flatmap(industry -> industry.getCompanies().stream())
.filter(Objects::nonNull)
.flatmap(company -> company.getDivisions().stream())
.filter(Objects::nonNull)
.flatmap(division -> division.getGroups().stream())
.map(group -> group.getGroupName)
.collect(Collectors.joining("/")));
This code seems to flawed in someway. Also, I am not sure where to add the statement that if Industry cannot retrieve any groupName, rather than concatenate all groupName into one string simply return a string "null".
What is the proper way to use Java 8 stream in my situation?
Thanks.
Collectors.joining(…) is based on the class StringJoiner. It offers its delimiter, prefix, and suffix features, but unfortunately not the ability to provide the empty value.
To add that feature, we’ll have to re-implement Collectors.joining, which thankfully is not so hard when using StringJoiner.
Change the last line of your stream operation
.collect(Collectors.joining("/"));
to
.filter(Objects::nonNull) // elide all null elements
.collect(()->new StringJoiner("/", "", "").setEmptyValue("null"), // use "null" when empty
StringJoiner::add, StringJoiner::merge).toString();
I understood your question as pretty much anything can be null. In this case you could create your own function to deal with this. I made one as such:
/**
* Creates a stream function for the provided collection function which ignores all null values.
* Will filter out null values passed into the collection function and null values from the resulting stream
* #param collectionFn
* #param <T>
* #param <R>
* #return
*/
public static <T, R> Function<T, Stream<R>> nullSafeMapper(Function<T, Collection<R>> collectionFn) {
return (v) -> Optional.ofNullable(v)
.map(collectionFn)
.map(Collection::stream)
.orElse(Stream.empty())
.filter(Objects::nonNull);
}
Basically its completely null safe, filtering out anything which is null in the input and output. and could be used as such:
industries.stream()
.flatMap(SO46101593.nullSafeMapper(Industry::getCompanies))
.flatMap(SO46101593.nullSafeMapper(Company::getDivisions))
.flatMap(SO46101593.nullSafeMapper(Division::getGroups))
.map(group -> group.getGroupName())
.filter(Objects::nonNull) // filter out null group names
.collect(Collectors.joining("/"));
You could also take that logic and push it down directly into your expression but since it has to be repeated 3 times it gets a bit... verbose and repetitive
Here is an example with null checks:
String s = industries.stream()
.filter( i -> i.getCompanies() != null ).flatMap( i -> i.getCompanies().stream() )
.filter( c -> c != null && c.getDivisions() != null ).flatMap( c -> c.getDivisions().stream() )
.filter( d -> d != null && d.getGroups() != null ).flatMap( d -> d.getGroups().stream() )
.filter( g -> g != null && g.getGroupName() != null ).map( g -> g.getGroupName() )
.collect( Collectors.joining("/") );
You can replace Collectors.joining("/") with Holger's example.
This should do it:
Stream.of(industry)
.map(Industry::getCompanies).filter(Objects::nonNull)
.flatMap(Set::stream)
.map(Company::getDivisions).filter(Objects::nonNull)
.flatMap(Set::stream)
.map(Division::getGroups).filter(Objects::nonNull)
.flatMap(Set::stream)
.map(Group::getGroupName).filter(Objects::nonNull)
.collect(Collectors.collectingAndThen(Collectors.joining("/"),
names -> names.isEmpty() ? "null" : names));
I'm assuming industry.stream() is incorrect, since you say you're working from "an instance of Industry". Instead, I make a Stream<Industry> with one element.
You need to do the null checks on the sets before you try to call stream on them. You're checking whether stream returns null, which is too late.
The final transform from empty result to "null" falls under the concept of the "finisher" function in Collector. Collectors.joining doesn't let you specify a finisher directly, but you can use Collectors.collectingAndThen to add a finisher to any existing Collector.

Convert OptionalDouble to Optional <java.lang.Double>

I have a method that builds a list and I want it to return the average of the list as an Optional value.
However, when I calculate the average value using Java 8, I always get the return value as an OptionalDouble.
How do I convert
OptionalDouble to Optional<Double>?
Below are my code for average calculation:
private static Optional<Double> averageListValue() {
// Build list
List<Double> testList = new ArrayList<>();
testList.add(...);
...
...
return testList.stream().mapToDouble(value -> value).average();
}
Thanks.
I'd go for this approach:
private static Optional<Double> convert(OptionalDouble od) {
return od.isPresent() ?
Optional.of(od.getAsDouble()) : Optional.empty();
}
A slight variation on #Andremoniy's answer is to skip the DoubleStream and use the averagingDouble() collector:
if (testList.isEmpty()) {
return Optional.empty();
}
return Optional.of(testList.stream().collect(Collector.averagingDouble()));
Or consider whether 0 is a valid return value for an empty list, and possibly skip the Optional entirely.
BTW, I found another solution, which has most simple form.
I've started thinking about: when result of average can be empty? Only when list it self is empty, right? So if we are sure that list is not empty, than we can safely do getAsDouble():
return Optional.ofNullable(testList.isEmpty() ? null :
testList.stream().mapToDouble(v -> v).average().getAsDouble())
(from performance point of view this could be more efficient than creating additional lambda wrappers, as was suggested in similar answers.)
This is quite an old question, but in newer versions of Java you can:
// Given an OptionalDouble
var myOptionalDouble = OptionalDouble.of(3.0d);
// Convert to Optional<Double>
var myOptional = myOptionalDouble.stream().boxed().findFirst();
The reverse is similarly easy:
var myOptional = Optional.of(Double.valueOf(3.0d));
var myOptionalDouble = myOptional.stream().mapToDouble(t -> t).findFirst();
I don't know if there exists a neat solution, but ths should work:
OptionalDouble optionalDouble = testList.stream().mapToDouble(value -> value).average();
return Optional.ofNullable(optionalDouble.isPresent() ? optionalDouble.getAsDouble() : null);
Just for fun, I wanted to see if it could be written in a single statement, without any need for an OptionalDouble temp variable. Here is the best I came up with:
return testList.stream().collect(Collectors.collectingAndThen(
Collectors.summarizingDouble(Double::doubleValue),
stat -> Optional.ofNullable(stat.getCount()>0 ? stat.getAverage() : null)));
I came to this "one line" (single statement) solution:
return ((Function<OptionalDouble, Optional<Double>>) od
-> od.isPresent() ? Optional.of(od.getAsDouble()) : Optional.empty())
.apply(testList.stream().mapToDouble(v -> v).average());
BTW, just for sake of minimalism, if you will do static import:
import static java.util.Optional.*;
you can omit Optional., what makes it a little bit less messy.

Check if something is equal to null or empty using lambdas

I have a class that counts the average number of words in a sentence using Lambdas in java. The problem that I'm having is that if corp is null or is empty I need to return 0. Currently I am getting NaN if corp is either null or empty. The rest of my code does what it should, but I cannot figure this part out.
public class AverageNumberOfWordsPerSentence extends TextMetric<Double> {
#Override
public Double apply(final Corpus corp) {
Sentences sentences = new Sentences();
List<String> sentenceList = sentences.apply(corp);
LongSummaryStatistics lss = corp.texts().stream()
.map(blob -> blob.text())
.flatMap(string -> stream
(string.split("\\W+")))
.filter(string -> !string.isEmpty())
.mapToLong(String::length)
.summaryStatistics();
return (double)lss.getCount() /
sentenceList.size();
}
Change the return statement to:
return sentenceList.isEmpty() ? 0.0 : (double)lss.getCount() / sentenceList.size();
And then hope that whoever told you “not to use control structures” will accept it. Strictly speaking, the ?: operator is a control structure, but it doesn’t have a keyword like if or while.
If I've got you right, then you need to use java.util.Optional:
class AverageNumberOfWordsPerSentence {
public Double apply(final Corpus corp) {
return Optional.of(corp).map(corp -> {
Sentences sentences = new Sentences();
List<String> sentenceList = sentences.apply(corp);
LongSummaryStatistics lss = corp.texts().stream()
.map(blob -> blob.text())
.flatMap(string -> stream
(string.split("\\W+")))
.filter(string -> !string.isEmpty())
.mapToLong(String::length)
.summaryStatistics();
return (double) lss.getCount() /
sentenceList.size();
}).orElse(0);
}
}
`
From the OP's comment,
Corpus corpus = new Corpus("King", text); So if the string where king is is empty or null then I have to return 0.
it appears that there needs to be some conditional logic that bypasses the stream if a member of Corpus is null or empty. The OP didn't say what the name of the property that holds "King" is, so I'll assume it is getKing() for now.
Like what #nikelin posted, Optional will help you here. Using Optional.filter() you can branch without using control structors. For example, you could do this to test to see if the "king" value is there and if it is null or an empty string, return 0, otherwise get the text metrics:
return Optional.of(corp)
.filter(c -> c.getKing() != null && !c.getKing().isEmpty()) // skip to the orElse() if it is null or empty)
.map(c -> c.texts()) // or .map(Corpus::texts)
.map(t -> t.stream()...blah blah get the word count...)
.map(count -> (double) count / sentences)
.orElse(0.0)
Any sequence of successive .map() operations can be combined into one, your choice.
If the initial Optional.filter finds that your "king" property is not null or empty, the stream operation the stream operation proceeds, getting the texts and calculating the word count as you specified already. It then maps the word count to sentenceCount/wordCount and returns that, but if your king property is null, the filter will leave the Optional empty, the map operations will be skipped, and the value in orElse(0.0) will be returned instead.

How to iterate nested for loops referring to parent elements using Java 8 streams?

I want to iterate nested lists using java8 streams, and extract some results of the lists on first match.
Unfortunately I have to also get a values from the parent content if a child element matches the filter.
How could I do this?
java7
Result result = new Result();
//find first match and pupulate the result object.
for (FirstNode first : response.getFirstNodes()) {
for (SndNode snd : first.getSndNodes()) {
if (snd.isValid()) {
result.setKey(first.getKey());
result.setContent(snd.getContent());
return;
}
}
}
java8
response.getFirstNodes().stream()
.flatMap(first -> first.getSndNodes())
.filter(snd -> snd.isValid())
.findFirst()
.ifPresent(???); //cannot access snd.getContent() here
When you need both values and want to use flatMap (as required when you want to perform a short-circuit operation like findFirst), you have to map to an object holding both values
response.getFirstNodes().stream()
.flatMap(first->first.getSndNodes().stream()
.map(snd->new AbstractMap.SimpleImmutableEntry<>(first, snd)))
.filter(e->e.getValue().isValid())
.findFirst().ifPresent(e-> {
result.setKey(e.getKey().getKey());
result.setContent(e.getValue().getContent());
});
In order to use standard classes only, I use a Map.Entry as Pair type whereas a real Pair type might look more concise.
In this specific use case, you can move the filter operation to the inner stream
response.getFirstNodes().stream()
.flatMap(first->first.getSndNodes().stream()
.filter(snd->snd.isValid())
.map(snd->new AbstractMap.SimpleImmutableEntry<>(first, snd)))
.findFirst().ifPresent(e-> {
result.setKey(e.getKey().getKey());
result.setContent(e.getValue().getContent());
});
which has the neat effect that only for the one matching item, a Map.Entry instance will be created (well, should as the current implementation is not as lazy as it should but even then it will still create lesser objects than with the first variant).
It should be like this:
Edit: Thanks Holger for pointing out that the code won't stop at the first valid FirstNode
response.getFirstNodes().stream()
.filter(it -> {it.getSndNodes().stream().filter(SndNode::isValid).findFirst(); return true;})
.findFirst()
.ifPresent(first -> first.getSndNodes().stream().filter(SndNode::isValid).findFirst().ifPresent(snd -> {
result.setKey(first.getKey());
result.setContent(snd.getContent());
}));
A test can be found here

Categories