I have an Optional of List of a class, i.e.: Optional<List<MyEntity>> opListEntity
I needed to map all MyEntity to MyEntityDto when Optional is present. In case Optional is empty, I'll return an empty ArrayList.
Approach 1 (Non-Functional):
Note: myEntityMapper is an object of a mapper class, which maps MyEntity to MyEntityDto.
List<MyEntityDto> res;
if (opListEntity.isPresent()) {
res = opListEntity.get().stream()
.map(myEntityMapper::entityToDto)
.collect(Collectors.toList());
} else {
res = new ArrayList<>();
}
This approach is fine but IntelliJ suggests to convert it to a functional-style expression. I let IntelliJ do the conversion and this is what I get:
Approach 2 (function expression):
List<MyEntityDto> res = opListEntity.map(myEntities -> myEntities.stream()
.map(myEntityMapper::entityToDto)
.collect(Collectors.toList()))
.orElseGet(ArrayList::new);
What I don't understand is, In approach 2 @ line 1, why is there a map?
Let me explain a bit more. See the 3rd Approach:
Approach 3:
List<CustomerAddressEntity> myEntities = opListEntity
.orElseGet(ArrayList::new);
List<MyEntityDto> res = myEntities.stream()
.map(myEntityMapper::entityToDto)
.collect(Collectors.toList());
Approach 3 works fine, but if I try to convert approach 3 to approach 4, It doesn't work.
Approach 4:
List<MyEntityDto> res = opListEntity.stream()
.map(myEntityMapper::entityToDto)
.collect(Collectors.toList()))
.orElseGet(ArrayList::new);
why approach 4 doesn't work but approach 2 does?
what is the extra map doing in approach 2 @ line 1?
I think it becomes obvious if you indent the code a bit more to make it somewhat easier to spot:
List<MyEntityDto> res = opListEntity // Optional<List<MyEntity>>
.map(
myEntities -> myEntities.stream() // Stream<MyEntity>
.map(myEntityMapper::entityToDto) // Stream<MyEntityDto>
.collect(Collectors.toList()) // List<MyEntityDto>
)
.orElseGet(ArrayList::new); // List<MyEntityDto>
That is the Optional (if present) is mapped to the result of transforming the contained list to a stream, mapping the elements and building a collection, or (if empty) to a new empty list.
In your 4th approach opListEntity is of type Optional<List<MyEntity>>. Now it depends on the JDK version you're using. In JDK8 Optional has no stream() method.
List<MyEntityDto> res = opListEntity.stream() // there is no such method in JDK8
...
Since JDK9 there is a stream() method but it will of course return Stream<List<MyEntity>> as this is the type of the Optional. But for the mapping to work you'd need Stream<MyEntity>. To get this you could map the returned stream using the flatMap method:
List<MyEntityDto> res = opListEntity.stream() // Stream<List<MyEntity>>
.flatMap(List::stream) // Stream<MyEntity>
.map(myEntityMapper::entityToDto)
...