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();
The logic is very simple. First, we will initialize a variable sum to 0 that holds the sum of the list elements. Declare another element say average (avg), it holds the average of the list. We have created an instance of the ArrayList class and invoked the add() method to add the elements to the List.
To do this, all we need is the count of numbers seen so far in the stream, previous average, and new number. Let n be the count, prev_avg be the previous average and x be the new number being added. The average after including x number can be written as (prev_avg*n + x)/(n+1).
IntStream average() method in Java The average() method of the IntStream class in Java returns an OptionalDouble describing the arithmetic mean of elements of this stream, or an empty optional if this stream is empty. It gets the average of the elements of the stream.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With