如何用Java Stream API计算坐标路径长度且无需额外容器类?
问题
我有一个包含多个Coordinate对象的List,每个Coordinate对象包含x和y坐标,该类提供distanceTo(Coordinate coord)方法,用于返回当前对象与目标coord对象之间的距离。
现在需要计算由List<Coordinate> path表示的路径总长度,想通过Java Stream API实现,可能用到reduce、collect或mapToDouble等方法,但没找到合适的方案。曾找到类似问题的解答,但该方案需要新建类,对当前场景来说过于繁琐。
是否存在无需额外创建类的优雅实现方案?
示例代码:
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); // 基础实现方案 double pathLength = 0; for (int i = 0; i < path.size() - 1; i++) { pathLength += path.get(i).distanceTo(path.get(i + 1)); } // Stream实现位置 } 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); } } }
解决方案
方法1:IntStream索引遍历(推荐)
直接通过索引流获取相邻坐标对,计算距离后求和,逻辑与原for循环完全一致,简洁高效:
double totalLength = IntStream.range(0, path.size() - 1) .mapToDouble(i -> path.get(i).distanceTo(path.get(i + 1))) .sum();
这种方式无需额外类,可读性强,性能和传统循环接近,是最优选择。
方法2:用AtomicReference记录前序坐标
借助AtomicReference存储前一个坐标元素,遍历过程中计算相邻距离:
import java.util.concurrent.atomic.AtomicReference; // ... AtomicReference<Coordinate> prevCoord = new AtomicReference<>(); double totalLength = path.stream() .mapToDouble(curr -> { Coordinate prev = prevCoord.getAndSet(curr); return prev == null ? 0 : prev.distanceTo(curr); }) .sum();
第一个元素因无前序坐标返回0,后续元素均计算与前一个坐标的距离,最终求和得到总长度。AtomicReference支持并行流场景的线程安全,顺序流下也能正常工作。
方法3:reduce结合数组临时存储
利用reduce的累积特性,用数组存储总长度和前序坐标(避免新建类),但逻辑相对复杂:
double totalLength = path.stream() .reduce(new double[]{0, 0, 0}, // 数组元素:[总长度, 前序x, 前序y] (acc, curr) -> { // 处理第一个元素,仅记录坐标 if (acc[1] == 0 && acc[2] == 0 && path.indexOf(curr) == 0) { acc[1] = curr.x; acc[2] = curr.y; return acc; } // 非第一个元素,计算距离并累加 acc[0] += new Coordinate((int) acc[1], (int) acc[2]).distanceTo(curr); // 更新前序坐标为当前坐标 acc[1] = curr.x; acc[2] = curr.y; return acc; }, // 并行流合并逻辑,单线程可忽略 (acc1, acc2) -> { acc1[0] += acc2[0]; return acc1; })[0];
注意:path.indexOf(curr)在大列表中会有性能损耗,仅适合小数据量场景,实际开发中不推荐这种方式。
内容的提问来源于stack exchange,提问作者Wewius
相关产品推荐
相关产品推荐

