Since you need to flatten the stream data, i.e. turn each element of type OffenceEntity
into a group of elements, mapToInt()
isn't the right operation. For that purpose you can use flatMap()
(or its flavor flatMapToInt()
).
To differentiate between the two operations map()
and flatMap()
, remember a simple principle:
- when you need one-to-one transformation, use
map()
or it's flavors
- for one-to-many transformation -
flatMap()
or it's flavors.
Since Java 16 we can also utilize mapMulty()
to flatten the data in the stream. But it's rather a special purpose tool than a common option. Apart from flattening this operation allows to filter out stream elements so in contrast to flatMap()
which turns an element into 1+
(one or more) elements, mapMulty()
produces 0+
(zero or more) elements.
flatMap()
flatMap()
expects as an argument a function, that takes an element and produces a stream (which is also important distinction between map
and flatMap
).
After flattening stream data, we need to filter OffenceDetailsEntity
objects having a suitable date and extract penaltyPoints
. Which can be done using filter()
+ mapToInt()
.
public int countPenaltyPointsTest(Set<OffenceEntity> offences, LocalDate date) {
return offences.stream() // Stream<OffenceEntity>
.flatMap(offence -> offence.getDetails().stream()) // Stream<OffenceDetailsEntity>
.filter(ode -> date.isAfter(ode.getStartDate()) // Stream<OffenceDetailsEntity>
&& date.isBefore(ode.getEndDate()))
.mapToInt(OffenceDetailsEntity::getPenaltyPoints) // IntStream
.sum();
}
mapMulty()
This operation allows incorporating imperative programming features (i.e. loops and conditional statements) into the stream pipeline.
As mentioned before, it's a special purpose tool with a lot of peculiarities which should be applied mindfully.
Here's quote from the API Note regurding when to use mapMulty()
:
This method is preferable to flatMap
in the following circumstances:
- When replacing each stream element with a small (possibly zero) number of elements. Using this method avoids the overhead of creating
a new
Stream
instance for every group of result elements, as required
by flatMap
.
- When it is easier to use an imperative approach for generating result elements than it is to return them in the form of a
Stream
.
For instance, mapMulty()
is capable to substitute a combination flatMap()
+ filter()
, or multiple flatMap()
operations.
It expects an argument of type BiConsumer
, i.e. a consumer, which in turn takes two arguments: stream element and a consumer of the resulting type. Each value offered to the consumer becomes a new stream element, replacing the initial element.
Here's how this method can be implemented using mapMultyToInt()
:
public int countPenaltyPointsTest(Set<OffenceEntity> offences, LocalDate date) {
return offences.stream()
.mapMultiToInt((offence, consumer) ->
offence.getDetails().forEach(ode -> {
if (date.isAfter(ode.getStartDate()) && date.isBefore(ode.getEndDate()))
consumer.accept(ode.getPenaltyPoints());
}))
.sum();
}
For more examples and information on how to use mapMulty()
, have a look at this question.