我想使用lambda表达式而不是for循环生成数字列表。
所以,假设我要生成一个100以下的所有三角数列表。三角数是遵循以下公式的数字:(n * n + n)/ 2
这样做的最佳方式是什么? 目前我有这个:
Stream.iterate(1, n -> n + 1).limit(100)
.map(n -> (n * n + n) / 2)
.filter(a -> a < 100)
.map(a -> a + "")
.collect(Collectors.joining(", ", "Numbers: ", "."));
但这似乎不必要的计算量太多了。我迭代n超过1到100(因为我假设我不知道n的最大值是什么),然后我映射该列表的三角数函数然后我检查哪些数字在100以下。是否有更有效的方法这样做? 另外:我可以仅使用Stream的迭代函数生成三角形数字而不是使用迭代,限制然后映射吗?
编辑: 所以这里的要点是:一旦三角形数字之一超过100,如何计算traingle数字? 通常我会这样写:
ArrayList<Integer> triangles = new ArrayList<>();
for (int n=1;true;n++) {
int num = (n*n+n)/2;
if (num>100) break;
triangles.add(num);
}
一旦三角形数超过100就停止,这是非常有效的;如何在lambda表达式中保留这种效率?
答案 0 :(得分:6)
一般情况下,您正在寻找的是需要的时间。 不幸的是,它在Java 8流中没有默认实现。 请参阅question about take-while。
答案 1 :(得分:-2)
如果您要做的就是将给定的序列转换为三角形(如您所述),这样做会更加简单。
List<Integer> l = IntStream.rangeClosed(1, 100)
.mapToObj(n -> (n*n + n) / 2)
.collect(Collectors.toList());
原始流包装器需要额外的步骤来向上转换为对象,因此mapToObj
方法。
如果您在达到100时想要停止过滤,我能想到的最简单的方法是
IntFunction<Integer> calc =n -> (n*n+n) / 2;
List<Integer> l = IntStream.rangeClosed(1, 100)
.filter(n -> calc.apply(n) < 100)
.mapToObj(calc)
.collect(Collectors.toList());
根据您问题的变化,我认为这一点也非常重要。如果你想要镜像你曾经做过的事情,那将是这样的:
List<Integer> results = new ArrayList<>(100);
IntStream.rangeClosed(1, 100).forEach(i -> {
int tri =calc.apply(i);
if(tri < 100) {
results.add(tri);
}
});
值得指出的是,流不一定是有序的(尽管默认实现遵循迭代器)。如果将其转换为并行流,您将看到差异(以及流的功能)。您无法从执行中解脱,因为您已经假设了一定数量的处理订单。通过提前过滤(以我的第二种形式),您将确保在最终计算之前最终只有13个条目的结果流。将此并行选项作为注释。
List<Integer> l = IntStream.rangeClosed(1, 100).parallel()
.filter(n -> calc.apply(n) < 100)
.mapToObj(calc)
.collect(Collectors.toList());
您会看到他们仍然订购了,但他们的计算是在多个线程上完成的。