【问题标题】:Lambda expressions in Java 8Java 8 中的 Lambda 表达式
【发布时间】:2015-06-07 13:16:59
【问题描述】:

我想使用 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,怎么会停止计算? 通常我会这样写:

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 表达式中保持这种效率?

【问题讨论】:

  • Stream.iterate(1, n-&gt;n+1).limit(100) 可以重写为IntStream.rangeClosed(1, 100),这可能更具可读性。
  • 你为什么同时使用限制和过滤器?我相信第二个过滤器会根据计算限制输出,所以你只会得到小于 100 的结果,而不是小于 100 的输入。
  • 重点是什么?还是只是好奇?

标签: java lambda


【解决方案1】:

在一般情况下,您正在寻找的是花时间。 不幸的是,它在 Java 8 流中没有默认实现。 见question about take-while

【讨论】:

  • takeWhile 是在 Java 9 中添加的。
【解决方案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());

您会看到它们仍然是有序的,但它们的计算是在多个线程上完成的。

【讨论】:

  • OP 询问当(n*n + n) / 2 的值将开始大于 100 时如何停止流式传输,因为需要过滤大量案例。所以问题是“如果我们知道不需要某个第 n 个值之后的值,我们怎么能跳过它们”。
  • 欧普还没有回答任何问题。目前还不清楚他要什么。
  • @JohnAment 如果你不清楚问题是什么,那是不回答的好理由。
  • 您的解决方案比 OP 的解决方案差:它对导致结果
  • @JohnAment 这不是问题所在。您使用流的代码比使用流的 OP 的代码慢,并且它没有执行 OP 想要的操作:一旦找到结果 >= 100,就停止迭代并进行计算。
猜你喜欢
  • 2017-11-16
  • 1970-01-01
  • 1970-01-01
  • 2015-06-01
  • 2015-07-05
  • 1970-01-01
  • 2015-04-17
  • 1970-01-01
  • 2019-01-16
相关资源
最近更新 更多