我的原始代码是:
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));
对于连续整数流的特定情况,可以将iterate
和limit
替换为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.iterate
和IntPredicate
作为:
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);