如何使用java 8流获取路径长度

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

我有

List<Vector3D>
,其中
Vector3D
是一个坐标。我想找到列表中
Vector3D
元素之间所有距离的总和。我想使用 java 8 流找到它。我尝试使用
reduce
但它对我没有帮助。

更新:

Vector3D
有方法
double distance(Vector3D)
可以找到两个位置之间的距离。例如我有 (1,0,0) (2,0,0) (3,0,0) 的列表。结果我想找到这条路径的长度。是 3。

如果我们使用 java 7 或更低版本,我们必须这样做:

public static double calcPathLength(List<Vector3D> path){
    double length = 0d;
    for (int i=0; i< path.size()-1; i++){
        length += path.get(i).distance(path.get(i+1));
    }
    return length;
}
java collections java-8 java-stream
3个回答
3
投票

您正在执行的操作称为可变归约

Pshemo 的回答展示了如何通过提供三个必要的函数来临时实现此类操作。但是,当所有三个函数都由专用类实现时,在实现

Collector
的类中实现这些函数可能会很有用,以便于重用:

public class Distance implements Collector<Vector3D, Distance.Helper, Double> {

    public static final Distance COLLECTOR = new Distance();

    static final class Helper {
        private double sum = 0;
        private Vector3D first = null, previous = null;
    }
    public Set<Characteristics> characteristics() {
        return Collections.emptySet();
    }
    public Supplier<Helper> supplier() {
        return Helper::new;
    }
    public BiConsumer<Helper, Vector3D> accumulator() {
        return (helper,vector3d)-> {
            if (helper.previous != null)
                helper.sum += vector3d.distance(helper.previous);
            else helper.first = vector3d;
            helper.previous = vector3d;
        };
    }
    public BinaryOperator<Helper> combiner() {
        return (h1,h2)-> {
            h2.sum += h1.sum;
            if(h1.previous!=null && h2.first!=null) {
                h2.sum += h1.previous.distance(h2.first);
                h2.first=h1.first;
            }
            return h2;
        };
    }
    public Function<Helper, Double> finisher() {
        return helper -> helper.sum;
    }
}

您将从 ad-hoc 版本中认出这三个功能。 New 是第四个函数

finisher
,它允许指定如何从可变容器中提取最终结果,因此我们不需要
getSum()
调用。

用例简化为:

List<Vector3D> list;
//…
double distance=list.stream().collect(Distance.COLLECTOR);

2
投票

其中一个选项是创建一些辅助类,它会记住以前使用的向量,并基于它计算它与当前向量之间的差异。这个类可能看起来像

class DistanceHelper {
    private double sum = 0;
    private Vector3D first = null;
    private Vector3D last = null;

    public void add(Vector3D vector3d) {
        if (first == null)
            first = vector3d;
        if (last != null)
            sum += vector3d.distance(last);
        last = vector3d;
    }

    public void combine(DistanceHelper otherHelper) {
        //add distance of path from current thread with distance of path
        //from other thread
        sum += otherHelper.sum;
        //also add distance between paths handled by separate threads like
        // when path of Thread1 is A->B and Thread2 is C->D then we need to 
        // include path from `B` to `C`
        if (this.last!=null && otherHelper.first!=null)
            sum += this.last.distance(otherHelper.first);
        this.last = otherHelper.last;
    }

    public double getSum() {
        return sum;
    }
}

您可以将其与

combine
一起使用,而不是
reduce

double sum = list
        .stream()//or parallelStream()
        .collect(DistanceHelper::new, DistanceHelper::add,
                DistanceHelper::combine).getSum();

0
投票

这可以使用 Java 22 Stream Gatherers 预览语言功能来完成,将流转换为相邻坐标对的滑动窗口。它是一个预览功能,因此通常不应该在生产代码中使用它,直到它升级为最终功能。

return path.stream().gather(Gatherers.windowSliding(2))
        .mapToDouble(pair -> pair.getFirst().distance(pair.getLast()))
        .sum();

这使用

Gatherers.windowSliding
Stream<Coordinate>
转换为每对连续坐标的 2 元素列表
Stream<List<Coordinate>>
。然后使用
Stream.mapToDouble
检索每对之间的距离,并使用
DoubleStream.sum
求和。

示例

Stream<Vector3D> vectors = path.stream();

[
  (1, 0, 0),
  (2, 0, 0),
  (3, 0, 0)
]

->

Stream<List<Vector3D>> pairs = vectors.gather(Gatherers.windowSliding(2))

[
  [(1, 0, 0), (2, 0, 0)],
  [(2, 0, 0), (3, 0, 0)]
]

->

DoubleStream distances =
        pairs.mapToDouble(pair -> pair.getFirst().distanceTo(pair.getLast()));

[
  1.0,
  1.0
]

->

double length = distances.sum();

2.0

Java文档

Gatherer

将输入元素流转换为输出元素流的中间操作,可以选择在到达上游末尾时应用最终操作。 […]

[…]

聚集操作的例子有很多,包括但不限于:将元素分组(窗口函数);对连续相似的元素进行去重;增量累加功能(前缀扫描);增量重新排序功能等。类

Gatherers
提供了常见收集操作的实现。

Stream.gather

返回一个流,其中包含将给定收集器应用于该流的元素的结果。

Gatherers.windowSliding

返回一个 Gatherer,它将元素收集到给定大小的窗口(遇到有序的元素组)中,其中每个后续窗口都包含前一个窗口的所有元素(除了最近的元素之外),并在流中添加下一个元素。 […]

示例:

// will contain: [[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8]]
List<List<Integer>> windows2 =
    Stream.of(1,2,3,4,5,6,7,8).gather(Gatherers.windowSliding(2)).toList();

// will contain: [[1, 2, 3, 4, 5, 6], [2, 3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8]]
List<List<Integer>> windows6 =
    Stream.of(1,2,3,4,5,6,7,8).gather(Gatherers.windowSliding(6)).toList();
© www.soinside.com 2019 - 2024. All rights reserved.