Reputation: 335
I have a small method containing the following code:
final int year = getYear();
final Carrier carrier = getCarrier();
final CarrierMetrics metrics = new CarrierMetrics(carrier);
repository.getFlightStream(year)
.filter(flight -> flight.getCarrier().equals(carrier))
.forEach(flight -> {
metrics.addFlight(flight);
printf("%,10d\t%,10d\t%,10d\t%,10d\r",
metrics.getTotalFlights(),
metrics.getTotalCancelled(),
metrics.getTotalDiverted(),
metrics.getAirports().size()
);
});
Hopefully it's obvious that what I am doing is accumulating metrics while processing each Flight in the stream. The code does work but I'm wondering if there is a better (more functional) way to implement this behavior, possibly using a Collector. Any feedback is appreciated.
Thanks,
-Tony
Upvotes: 1
Views: 111
Reputation: 124804
If the printing in the forEach
is important,
then your current solution is good as it is.
forEach
is designed for side effects,
and you have two side effects: adding metrics to CarrierMetrics
instance and printing.
If the printing in the forEach
is only for debugging,
and not intended in your final solution,
then a more functional implementation would be to collect results directly into a CarrierMetrics
instance,
instead of initializing an instance first and manually adding with forEach
.
You can use the overload of collect(...)
that takes 3 arguments:
Supplier<CarrierMetrics>
to create an initial CarrierMetrics
instance, which will be used as an accumulatorBiConsumer<CarrierMetrics, Flight>
that pass a Flight
instance to the accumulator
Flight
is just a guess based on the code you shared. It's the type of the stream (and so the type of the parameter of CarrierMetrics.addFlight
method)BiConsumer<CarrierMetrics, CarrierMetrics>
that combines multiple accumulators in case of a parallel streamLike this:
final int year = getYear();
final CarrierMetrics metrics = repository.getFlightStream(year)
.filter(flight -> flight.getCarrier().equals(carrier))
.collect(CarrierMetrics::new, CarrierMetrics::addFlight, (a1, a2) -> {});
The third argument, the combiner, is a dummy,
you will need to fix that.
Its implementation should combine the two CarrierMetrics
parameters into the first one.
(I cannot give a concrete example, because you haven't shared enough details about CarrierMetrics
to be able to see how to do.
But to give some example, in case of a List
accumulators,
the implementation could be (a1, a2) -> a1.addAll(a2)
.)
(Lastly, this example assumes that CarrierMetrics
has a parameterless constructor, for the CarrierMetrics::new
reference to work.
If there is no such constructor, you can use an appropriate lambda expression, such as () -> new CarrierMetrics(...)
.)
Upvotes: 5
Reputation: 764
If CarrierMetrics
exposing a addAll(List<Flight> flights)
method you can do the following:
List<Flight> flights = repository.getFlightStream(year)
.filter(flight -> flight.getCarrier().equals(carrier))
.collect(Collectors.toList());
metrics.addAll(flights);
Upvotes: 0