Java chaining map methods and using optional - java

I am using Java 8
method1() -> returns a Map<String,Map<String,Set<String>>>.
If I chain the method calls this way -> method1().get("A").get("B") a NullPointerException could happen.
So I am using the following strategy to avoid using the traditional if( != null) code:
Optional.ofNullable(method1()).map(x -> x.get("A")).map(y -> y.get("B")) but the this code return an Optional and I need that returns the Set<String>.
How can I cast it, and in case of null (in the case of the get methods returns null) how returns null?
Thanks in advance.

Polishing the code by gamgoon a little bit:
Set<String> result = Optional.of(method1())
.map(x -> x.get("A"))
.map(y -> y.get("B"))
.orElse(Collections.emptySet());
System.out.println(result);
Output in case either key is not in a map where looked up:
[]
You may recognize the result of printing an empty collection. In places where you want a collection, you should not accept a null. Use an empty collection to signify that there are no elements. In the same vein I have assumed that your method1() may return an empty map but not null. So we don’t need ofNullable() when converting into an Optional — the simple of() is fine.

use orElse like
Set<String> result = Optional.ofNullable(method1())
.map(x -> x.get("A"))
.map(y -> y.get("B"))
.orElse(null);

Related

I need to understand syntax of this java code [duplicate]

This question already has answers here:
Why can a method be called on another method?
(7 answers)
Java Pass Method as Parameter
(17 answers)
Closed 1 year ago.
Can some please help me with syntax of following java code?
list.stream()
.filter(name -> name.startsWith("f"))
.map(String::toUpperCase)
.sorted()
.forEach(System.out::println);`
is method filter(name -> name.startsWith("f") is being called on the result of list.stream() and then .map(String::toUpperCase) is being called on its result ? What version of java is this? Where I can read about this more?
Your code snippet is a Java stream, and consists of a pipeline of several processing steps. Here is an explanation:
list.stream()
.filter(name -> name.startsWith("f")) // retain only names starting with 'f'
.map(String::toUpperCase) // map the name to uppercase
.sorted() // sort the stream ascending by name
.forEach(System.out::println); // print out each name
In plain English, this stream says to take an input list of names, remove any name not starting with 'f', uppercase all names, sort them ascending by name, and finally print out each name to the console.
In answer to your question, the lambda you're seeing (name -> name.startsWith("f")) is a feature added in Java 8. This version of Java also adds method references, which are essentially a quicker way to write many lambdas which merely call an existing method. The line .map(String::toUpperCase) could have been written as .map(name -> name.toUpperCase()). Similarly .forEach(System.out::println) could have been .forEach(name -> System.out.println(name)).
Documentation on Oracle.com for further reading:
Lambda Expressions
Method references
list.stream()
.filter(name -> name.startsWith("f"))
.map(String::toUpperCase)
.sorted()
.forEach(System.out::println);
The whole idea of Java streams is to enable functional-style operations on streams of elements, using Lambda experession and Method reference.
Method references are a special type of lambda expressions
In functional-style operations the execution is performed from left to right that means
stream() is called on result of list
filter() is called on result of list.stream()
map() is called on result of list.stream().filter(name -> name.startsWith("f"))
sorted() is called on result of list.stream().filter(name -> name.startsWith("f")).map(String::toUpperCase)
forEach is called on the result of list.stream().filter(name -> name.startsWith("f")).map(String::toUpperCase).sorted()
To explore your knowledge you can read from Oracle Java (version 8 onward) Documents
Specially for the topics Lambda expression and method reference :-
1. Lamda Expression
2. Method Reference
The stream is one of the functional element of Java which uses Lambda Expressions (unnamed function).
You have a list and convert it to a stream.
For each element you will use the filter function which removes the elements that do not safisfy the criteria. This selection criteria is given as a Lambda Function. Its argument is the element and it should return with a boolean value. So the name should start with 'f'.
Then you use the 'map' function to convert the elements of the list into another type using a Lambda Function again. In this case your list contains string and your LambadaFunction is the String::toUpperCase. This a short hand format of (name) -> {return String.toUpperCase(name);}. In other words, the toUpperCase function will be called with each element of the list and the 'map' will produce a new stream which contains the results.
Next you sort the stream.
Finally, Each element of the stream is given as an argument of the 'println' method.
The result should contains a sorted list of names starting with F.

Joining all subsets within a stream in Java optionals

This is the original piece of code:
Set<StatuteType> statuteTypes = registration.getStudent().getStudentStatutesSet()
.stream()
.map(StudentStatute_Base::getType)
.collect(Collectors.toSet());
I want to wrap everything in an Optional to avoid null pointers and all. If the student does not exist or the statutesSet does not exist.
What I have:
Set<StatuteType> statuteTypes = Optional.of(registration)
.map(Registration_Base::getStudent)
.map(student -> student.getStudentStatutesSet())
.flatMap(Collection::stream)
.map(StudentStatute_Base::getType)
.collect(Collectors.toSet())
.orElse(null);
Would something like this be in someway possible? I want to avoid null checks in this chain, and if there's any null just return a simple null as well instead getting an exception.
Normally what I think would be logical would be to use a flatMap as described here but it doesn't seem to be correct in this case, because the Optional flatmap returns an Optional.
Here's a simple way of doing it:
Set<StatuteType> statuteTypes = Optional.ofNullable(registration)
.map(Registration_Base::getStudent)
.map(student -> student.getStudentStatutesSet())
.map(Collection::stream)
.orElseGet(Stream::empty) // Exit Optional, enter stream
.map(StudentStatute_Base::getType)
.collect(Collectors.toSet());
However, it does not result in a null set. Collections should never be null, only empty. I would recommend this approach. The whole point of using an Optional object is so you never have to deal with null values.
Collection::stream does not return an Optional, so you should not use flatMap here. You should keep using map on the optional.
.map(Collection::stream) gives you an Optional<Stream<Statute>>. You seem to be trying to call the stream's map and collect methods on this. But you need to first call Optional.map before you can do that.
You should also use Optional.ofNullable if registration could be null:
Set<StatuteType> statuteTypes = Optional.ofNullable(registration)
.map(Registration_Base::getStudent)
.map(student -> student.getStudentStatutesSet())
.map(Collection::stream)
.map(x -> // Optional.map
x.map(StudentStatute_Base::getType) // Stream.map
.filter(Objects::nonNull) // I assume you want to filter out the statute types which are null?
.collect(Collectors.toSet())
)
.orElse(null);

Collect all values of a Set field

I have a collection which has a field of type Set with some values. I need to create a new set collecting all these values.
I am wondering if this is possible using lambda expressions.
Below is the code line :
Set<String> teacherId = batches.stream()
.filter(b -> !CollectionUtils.isEmpty(b.getTeacherIds()))
.map(b -> b.getTeacherIds())
.collect(Collectors.toSet());
The problem is post map operation, it contains a collection of set of strings. So collect operation returns a Set<Set<String>> but i am looking to aggregate all the values to a single set.
You need to use flatMap instead of map:
Set<String> teacherIds =
batches.stream()
.flatMap(b -> b.getTeacherIds().stream())
.collect(Collectors.toSet());
Note that the filtering is redundant for empty collections - streaming an empty collection will just result in an empty stream, which won't affect the final result.
If getTeacherIds() could return null, however, you'd still have to handle it. Using filter(Objects::nonNull) would suffice, and save you the dependency on Apache Commons.
You can use flatMap to obtain a flat Stream of all the values, and then collect to a Set<String>:
Set<String> teacherId =
batches.stream()
.filter(b -> !CollectionUtils.isEmpty(b.getTeacherIds()))
.flatMap(b -> b.getTeacherIds().stream())
.collect(Collectors.toSet());
If you care that that getTeacherIds() is not null, use it explicitly via !=, that CollectionUtils.isEmpty just hides stuff. Especially since if getTeacherIds() returns an Empty collection - that is handled just fine by flatMap, so to me that is not needed at all.
Set<String> teacherIds = batches
.stream()
.filter(x -> x.getTeacherIds() != null)
.flatMap(x -> x.getTeacherIds().stream())
.collect(Collectors.toSet());
I am wondering if this is possible using lambda expressions.
I capture the last fish, :).
Set<String> teacherIds = batches.stream()//v--- the class of `x`
.map(XClass::getTeacherIds)
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.collect(Collectors.toSet());
Note: I'm sorry I'm forget to tell you if the getTeacherIds copy the internal IDs to a new set of IDs, the code above is appropriate for you. since it is read the IDs from XClass once.

Flattening streams from a singleton Optional

This question is related to the Java 8 map and flatMap that is present both in Streams and Optionals. It is worth to note that C# has a similar construct named SelectMany.
I have learned about the two methods, in particular that in Streams you can use flatMap to get a Collection<T> from a Collection<Collection<T>> which is what I want.
In my example I have a nested class structure (from a DTD I have no control over) in which I want to compute a sum of values. I will not redact the class names for laziness.
class DatiTelematico {
private Adempimento adempimento;
}
class Adempimento {
private List<DatiNegozio> datiNegozio;
}
class DatiNegozio {
private List<Negozio> negozio;
}
class Negozio {
private List<Tassazione> tassazione;
}
class Tassazione {
private BigDecimal importo;
}
Given an Optional instance of a DatiTelematico class I would like to sum (importo) from telematico join adempimento join datiNegozio join negozio join tassazione.
The best I could do was to use nested lambdas and plain map method
optionalTelematico.map(DatiTelematico::getAdempimento)
.map(Adempimento::getDatiNegozio)
.map(l -> l.stream().map(DatiNegozio::getNegozio)
.map(n -> n.stream()
.map(Negozio::getTassazione)
.map(t -> t.stream()
.map(Tassazione::getImporto)
.reduce(BigDecimal.ZERO,
BigDecimal::add))
.reduce(BigDecimal.ZERO, BigDecimal::add))
.reduce(BigDecimal.ZERO, BigDecimal::add))
.orElse(BigDecimal.ZERO));
I have tried to start writing something like
optionalTelematico.map(DatiTelematico::getAdempimento)
.map(Adempimento::getDatiNegozio)
.map(l->l.stream().flatMap(n->n.getNegozio().stream().flatMap(s->s.getTassazione().stream().flatMap(Tassazione::getImporto)))....TBD
But then I get a compiler error
Method binding must be directly contained in a class (OTJLD A.3.1).
How do I smartly switch from an Optional<T> (singleton) to a Collection<U> that is to be summed over?
I am asking this to increase my knowledge of Java lambdas.
Both Optional and Stream actually only represent a single piece of data - in case of optional, that piece of data might be either absent or present, in case of stream, there may be other pieces of data coming before or after, but in current moment we have this piece only.
Now,
map method is essentially a kind of type transformation for both Optional and Stream: mapping takes a function I -> R, applying which one can make transformation Optional<I> -> Optional<R> (or Stream<I> -> Stream<R>).
flatMap method is a kind of transformation that can:
Transform optional value into another optional (possibly empty). That means a function type I -> Optional<R>
Transform each item in stream into another stream (having 0..n number of elements in it). That means function type I -> Stream<R>. Note that for streams this operation can change the number of elements that are contained in stream (but it won't change the fact that there always effectively one stream element being processed at a time).
In your particular case, by making transformation to optional, you may obtain up to Optional<List<DatiNegozio>> directly:
Optional<List<DatiNegozio>> optDatiNegozio = optionalDatiTelematico
.map(DatiTelematico::getAdempimento) // Optional<Adempimento>
.map(Adempimento::getDatiNegozio);
Every List<DatiNegozio> you can easily convert to Optional<BigDecimal> summing and accessing elements via Stream:
static Optional<BigDecimal> sumImporto(List<DatiNegozio> datiNegozio) {
return datiNegozio.stream() // Stream<DatiNegozio>
.map(DatiNegozio::getNegozio) // Stream<List<Negozio>>
// unroll stream of collections into a stream of collection elements
.flatMap(List::stream) // Stream<Negozio>
.map(Negozio::getTassazione) // Stream<List<Tassazione>>
// again, unroll stream of collections into a stream of collection elements
.flatMap(List::stream)
.map(Tassazione::getImporto) // Stream<BigDecimal>
// last thing we need to do is just reduce
.reduce(BigDecimal::add);
}
As you can see, second snippet allows you to convert List<DatiNegozio> into an Optional<BigDecimal>. After this, you have two options (stylistic choice):
There is a variant of reduce that yields BigDecimal instead of Optional<BigDecimal>:
.reduce(BigDecimal.ZERO, BigDecimal::add); // it yields concrete type instead of optional because even in case when there is no elements in stream, we can at least return value from which we started - ZERO
You can use second code snippet to produce Function that is usable in flatMap-ing an optional:
optionalDatiTelematico
.map(DatiTelematico::getAdempimento)
.map(Adempimento::getDatiNegozio)
.flatMap(Example::sumImporto) // reference to method from 2nd code snippet
.orElse(BigDecimal.ZERO); // what to do if we had Optional.empty at any point
You can use Collection.stream() method to convert Collection to Stream and use it in flatMap. So combination of .map(d -> d.getList()).flatMap(Collection::stream) returns stream for all internal lists of Stream<D>.
In your case it can looks like:
Optional.of(datiTelematico)
.map(DatiTelematico::getAdempimento)
.map(Adempimento::getDatiNegozio)
.map(Collection::stream)
.orElseGet(Stream::empty)
.map(DatiNegozio::getNegozio)
.flatMap(Collection::stream)
.map(Negozio::getTassazione)
.flatMap(Collection::stream)
.map(Tassazione::getImporto)
.reduce(BigDecimal.ZERO, BigDecimal::add);
Let say
Optional<DatiNegozio> abc = optionalTelematico.map(DatiTelematico::getAdempimento)
.map(Adempimento::getDatiNegozio)
Now when you say abc.map(xyz). xyz must be function which takes instance of DatiNegozio as one and only argument. In your case xyz is a lambda which takes one parameter l whose type should be DatiNegozio. You are now doing l.stream() which throws compile error beacsue stream() does not exist in l(instance ofDatiNegozio).

Nesting java8 streams to get a collection by Predicate

I am trying to get a collection of Objects, from a Collection using streams in Java8 based on some predicate
Here is what I have tried, but there is some syntax error in this.:
Collection<object> objectCollectionNew = objectCollection.stream().filter(o -> objectCollection.stream().filter( x- > x.isTrue == o.isTrue));
So basically I want to get a collection of objects out of the objectCollection based on my Predicate. Also I am unsure about how to collect this into my Collection<object>.
.collect(Collectors.to ?? )
Some help will be appreciated.
It's not entirely clear what you are trying to do, but if you want to find all elements of your collection whose isTrue is equal to isTrue of some other element of the collection, you would do it like this:
objectColelction.stream()
.filter(o -> objectColelction.stream().anyMatch(x -> x.isTrue == o.isTrue))
.collect(Collectors.toList());
It seems like you want to use a flatMap :
Collection<object> objectCollectionNew = objectCollection.stream()
.flatMap(o -> objectCollection.stream().filter(x -> x.isTrue == o.isTrue))
.collect(Collectors.toList());
I had a similar problem which looks like an error in the Eclipse syntax parser. I solved it by adding parenthesis
Collection<object> objectCollectionNew = objectCollection.stream()
.filter( x -> {return x.isTrue == o.isTrue;} )
.collect(Collectors.toList());
Just answering on the syntax aspect, not how to best solve your filtering itself.

Categories

Resources