Java 8中的Lambda表达式

11

我希望使用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的iterate函数生成三角形数,而不是使用iterate、limit和map?

编辑:因此,这里的主要问题是:如何在一个三角形数超过100时停止三角形数的计算?通常我会这样写:

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
Stream.iterate(1, n->n+1).limit(100) 可以重写为 IntStream.rangeClosed(1, 100),这样可能更易读。 - Pshemo
为什么你同时使用了限制和过滤器?我认为第二个过滤器将基于计算结果限制输出,因此你只会得到小于100的结果,而不是小于100的输入。 - John Ament
这有什么意义呢?还是只是出于好奇? - user319799
2个回答

6
通常情况下,你寻找的是 take-while。不幸的是,在Java 8流中它没有默认实现。有一个关于take-while的问题,请参见这里

4
takeWhile 方法是在 Java 9 中添加的。 - Brian Goetz

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

你会发现它们仍然是有序的,但是它们的计算是在多个线程上完成的。


1
OP 正在询问如何在 (n*n + n) / 2 的值开始变大并需要过滤的大量情况时停止流式传输。因此问题是:“如果我们知道某些 n 值之后的值将不再需要,我们怎样才能跳过它们?” - Pshemo
3
如果您不清楚问题是什么,那就不要回答是一个很好的理由。 - JB Nizet
你的解决方案比原始作者的还差:它对于每个小于100的数字执行两次计算,一次是过滤,一次是映射。 - JB Nizet
你更新后的代码仍然需要流式处理所有元素,并进行可能昂贵的计算。实际上,现在它甚至需要做两次。我不知道这是如何改进的。 - Pshemo
2
@JohnAment 这不是问题所在。你的代码使用流比 OP 的代码(也使用流)慢,并且它没有做到 OP 想要的:一旦找到结果 >= 100,就停止迭代和计算。 - JB Nizet
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接