Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to compute average of multiple numbers in sequence using Java 8 lambda

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();
like image 874
madhub Avatar asked Jan 13 '15 15:01

madhub


People also ask

How do you find the average of a list in Java?

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.

How do you find the average of a stream?

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).

How can we get an average of values of elements in a stream operation?

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.


1 Answers

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.

like image 195
Lukas Eder Avatar answered Oct 02 '22 15:10

Lukas Eder