如何使用 Java 8 lambda 按顺序计算多个数字的平均值

2022-01-22 00:00:00 java-8 java java-stream collectors

如果我有集合 Point ,如何在单次迭代中使用 Java 8 流计算 x,y 的平均值.

If I have collections Point , how do I compute average of x,y using Java 8 stream on a single iteration.

以下示例创建两个流 &在输入集合上迭代两次以计算 x & 的平均值是的.他们是否有任何方法可以使用 java 8 lambda 在单次迭代中计算平均 x,y:

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();

推荐答案

如果您不介意使用额外的库,我们在 jOOλ,最近.

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

在上面的代码中,Tuple.collectors()结合了几个java.util.stream.Collector 实例到单个 Collector 中,将单个值收集到 元组.

In the above code, Tuple.collectors() combines several java.util.stream.Collector instances into a single Collector that collects individual values into a Tuple.

这比任何其他解决方案都更加简洁和可重复使用.你要付出的代价是它目前在包装类型上运行,而不是原始的 double.我想我们必须等到 Java 10 和原始类型的项目 valhalla专攻泛型.

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)
        )
    );
}

其中 Tuple2 只是两个值的简单包装器.您不妨使用 AbstractMap.SimpleImmutableEntry 或类似的东西.

Where Tuple2 is just a simple wrapper for two values. You might as well use AbstractMap.SimpleImmutableEntry or something similar.

我还在an answer to another Stack Overflow question中详细介绍了这种技术.

I've also detailed this technique in an answer to another Stack Overflow question.

相关文章