我使用IntStream和forEach创建整数列表的方法有什么错误



我的原始代码是:

class ${
public static void main(String[] _) {
final List<Integer> ints = new ArrayList<>();
IntStream.iterate(0, i -> i++).limit(5).forEach(val -> ints.add(val));
System.out.println(ints);
}
}

我的期望是在控制台中看到以下内容:

[0, 1, 2, 3, 4]

但实际情况是:

[0, 0, 0, 0, 0]

这可能是一件非常简单的事情,但我错过了什么?

i++在递增之前具有值i。您需要使用前缀运算符。

IntStream.iterate(0, i -> ++i).limit(5).forEach(val -> ints.add(val));

事实上,不要那样做。没有理由突变i,它只是被扔掉了。使用无副作用版本。毕竟,这就是函数式编程背后的全部理念:避免副作用。

IntStream.iterate(0, i -> i + 1).limit(5).forEach(val -> ints.add(val));

对于连续整数流的特定情况,可以将iteratelimit替换为range:

IntStream.range(0, 5).forEach(val -> ints.add(val));

最后,最好将流收集到一个列表中,而不是使用forEach添加值。它直接表达了创建列表的意图,这再次避免了副作用。

List<Integer> ints = IntStream.range(0, 5).boxed().collect(Collectors.toList());

在将值传递给forEach时,使用后缀i++,而不是前缀++i。更改为以下内容将为您提供预期输出:

IntStream.iterate(0, i -> i + 1).limit(5).forEach(ints::add);

此外,用Java9+迭代和组合极限的另一种方法是使用IntStream.iterateIntPredicate作为:

IntStream.iterate(0, i -> i < 5, i -> i + 1).forEach(ints::add);

您需要返回递增的值。后缀增加了一个局部变量并返回了未增加的值。使用++i而非i++

final List<Integer> ints = new ArrayList<>();
IntStream.iterate(0, i -> ++i).limit(5).forEach(val -> ints.add(val));
System.out.println(ints);

编辑请参阅John Kugelman关于在函数式编程时使用非变异操作的文章。使用i + 1将创建一个新的基元,而不会更改参数变量。

您可以使用打印来查看发生了什么:

final List<Integer> ints = new ArrayList<>();
IntStream.iterate(0, i -> {
System.out.println(i);
return i++;
}).limit(5)
.forEach(val -> ints.add(val));
System.out.println(ints);

在这种情况下,i的值总是0,因为增量发生在返回值之后,正确的方法是

final List<Integer> ints = new ArrayList<>();
IntStream.iterate(0, i -> {
System.out.println(i);
return ++i;
}).limit(5)
.forEach(val -> ints.add(val));
System.out.println(ints);

最新更新