If I have collections Point , how do I compute average of x,y using Java 8 stream on a single iteration.
Following example creates two stream & iterates twice on the input collection to compute the average of x & y. Is their any way to computer average x,y on single iteration using java 8 lambda :
List<Point2D.Float> points =
Arrays.asList(new Point2D.Float(10.0f,11.0f), new Point2D.Float(1.0f,2.9f));
// java 8, iterates twice
double xAvg = points.stream().mapToDouble( p -> p.x).average().getAsDouble();
double yAvg = points.stream().mapToDouble( p -> p.y).average().getAsDouble();
If you don't mind using an additional library, we've added support for tuple collectors to jOOλ, recently.
Tuple2<Double, Double> avg = points.stream().collect(
Tuple.collectors(
Collectors.averagingDouble(p -> p.x),
Collectors.averagingDouble(p -> p.y)
)
);
In the above code, Tuple.collectors()
combines several java.util.stream.Collector
instances into a single Collector
that collects individual values into a Tuple
.
This is much more concise and reusable than any other solution. The price you'll pay is that this currently operates on wrapper types, instead of primitive double
. I guess we'll have to wait until Java 10 and project valhalla for primitive type specialisation in generics.
In case you want to roll your own, instead of creating a dependency, the relevant method looks like this:
static <T, A1, A2, D1, D2> Collector<T, Tuple2<A1, A2>, Tuple2<D1, D2>> collectors(
Collector<T, A1, D1> collector1
, Collector<T, A2, D2> collector2
) {
return Collector.of(
() -> tuple(
collector1.supplier().get()
, collector2.supplier().get()
),
(a, t) -> {
collector1.accumulator().accept(a.v1, t);
collector2.accumulator().accept(a.v2, t);
},
(a1, a2) -> tuple(
collector1.combiner().apply(a1.v1, a2.v1)
, collector2.combiner().apply(a1.v2, a2.v2)
),
a -> tuple(
collector1.finisher().apply(a.v1)
, collector2.finisher().apply(a.v2)
)
);
}
Where Tuple2
is just a simple wrapper for two values. You might as well use AbstractMap.SimpleImmutableEntry
or something similar.
I've also detailed this technique in an answer to another Stack Overflow question.