Java Streams:利用流函数使用坐标列表计算路径长度

问题描述 投票:0回答:2

我有一个坐标列表,每个坐标都包含 x 和 y 位置。该类还提供了 distanceTo(Coordinate coord) 方法,该方法返回坐标对象的 xy 位置到坐标坐标的距离。

现在我想计算表示为列表路径的路径的长度,并且我非常确定必须有一种巧妙的方法来使用流来执行此操作。可能通过reduce、collect或mapToDouble...我试图找到解决方案但无法找到。我能找到的最好的是这个类似的问题:How to get length of path using java 8 Streams

但是答案是,仅为这个类编写一个全新的类对于我的用例来说似乎有点过分了。

是否有一个优雅的解决方案来解决这个问题,而不涉及为距离创建一个全新的类?

任何帮助将不胜感激。谢谢你。

这是我想要做的一个简单的代码示例:

import java.util.List;

public class Main {
    public static void main(String[] args) {
        Coordinate c1 = new Coordinate(0, 0);
        Coordinate c2 = new Coordinate(0, 1);
        Coordinate c3 = new Coordinate(1, 1);
        Coordinate c4 = new Coordinate(1, 2);

        List<Coordinate> path = List.of(c1, c2, c3, c4);

        // Primitive solution I use currently
        double pathLength = 0;
        for (int i = 0; i < path.size() - 1; i++) {
            pathLength += path.get(i).distanceTo(path.get(i + 1));
        }

        // Using Steams
        

    }


    public static class Coordinate {
        private final int x;
        private final int y;

        public Coordinate(int x, int y) {
            this.x = x;
            this.y = y;
        }

        public double distanceTo(Coordinate coord) {
            return Math.hypot(x - coord.x, y - coord.y);
        }
    }
}
java stream
2个回答
0
投票

流非常有用,但并不总是适合所有情况 -

for
循环可能要容易得多。然而,可以使用流
reduce
操作来完成此计算,而不需要中间结果的额外持有者类:

double[] dist = new double[1];
path.stream().reduce((a,b) -> { dist[0] += a.distanceTo(b); return b; });

此reduce 的累加器函数将每个距离添加到局部数组变量中,并返回第二个坐标值

b
。这意味着按照列表序列顺序中的每对坐标都会调用reduce,如reduce的javadoc中提到的。

显然你不能在这里使用流

parallel()

,因为传递给累加器的配对不会按列表顺序排列。


0
投票
它不像 for 循环方法那么简单,但一种方法是使用自定义

Collector

 来减少流的总距离:

class PathDistance { Coordinate start; Coordinate end; double distance = 0.0; void addToPath(Coordinate newEnd) { if (start == null) { start = newEnd; } else { distance += end.distanceTo(newEnd); } end = newEnd; } PathDistance combine(PathDistance next) { if (start == null) { this.start = next.start; this.end = next.end; this.distance = next.distance; } else if (next.start != null) { distance += end.distanceTo(next.start) + next.distance; end = next.end; } return this; } } Collector<Coordinate, PathDistance, Double> collector = Collector.of( PathDistance::new, PathDistance::addToPath, PathDistance::combine, s -> s.distance); return path.stream().collect(collector);
该解决方案的一个优点是它是完全可并行的,这对于非常大的数据集可能是有益的。尽管我怀疑对于这个特定的用例,数据集将足够小,因此 for 循环可能是最合理的方法。

© www.soinside.com 2019 - 2024. All rights reserved.