Java 8中的Lambda表达式

时间:2015-06-07 13:16:59

标签: java lambda

我想使用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表达式中保留这种效率?

2 个答案:

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

您会看到他们仍然订购了,但他们的计算是在多个线程上完成的。