为什么在Lambda表达式中要使用++i而不是i++?

4

我知道在下面这个表达式中,使用 i++ 会导致无限流,i 会一直为0。我感到困惑的原因是我认为 i++ 返回值没有被使用,即使如此,它也不应该打断 i 的增量。

IntStream.iterate(0, i-> i<10, i-> ++i).forEach(....)

2
我对Java9不是很熟悉,但是IntStream.iterate(0, i-> i<10, i-> i+1).forEach(....)不应该更自然吗?你使用i++++i有什么理由吗? - ymonad
1
可能是Java:前缀/后缀递增/递减运算符?的重复。 - DarkCygnus
@ymonad i+1 也可以。我一直在想为什么 i++ 不行,现在清楚了。i++ 在一个函数中运行,函数返回值被赋给了 i... - Tiina
3个回答

5

通过查看Java 9的API:IntStreamhttp://download.java.net/java/jdk9/docs/api/java/util/stream/IntStream.html#iterate-int-java.util.function.IntPredicate-java.util.function.IntUnaryOperator-

最后一个函数(在您的情况下为i->++i)是确定下一个值的函数。

如果您使用i->i++,鉴于它是后缀递增运算符,i++在递增之前会计算出i。这意味着它始终返回相同的值(在您的情况下为种子0)。因此,它的作用就像您放置了i->i。请注意,在Java中,参数是按值传递的。因此,您在lambda中的递增不会影响调用者。

因此,hasNext谓词(第二个参数,在您的情况下为i->i<10)始终评估为true,从而为您提供所有零的无限流。


i -> i++ 中有一个非常令人困惑的序列。1) i 被赋值为 i,2) i 增加 1。但无论哪个先执行,除非 i 永远不会增加 1(就像 ++ 运算符总是这样做的),否则 i 不应该永远为 0。这是我不理解的部分。 - Tiina
1
第三个参数是一个函数,用于告诉 IntStream 下一个值是什么(给定前一个值)。在函数中做什么并不重要,只要返回某个值,那个返回的值就是下一个要使用的值。 - Adrian Shum
1
以另一种方式来看,对于你的情况 i -> i++ 就像是 i -> {int temp = i; i = i + 1; return temp}。由于参数是按值传递的,因此你的 i=i+1 对调用者没有影响。因此下一个值始终与原始值相同。 - Adrian Shum
啊,我明白了。 - Tiina

1

但我仍然想知道为什么 i++ 不会增加 i。这对我来说挑战了“常识”。 - Tiina
@Tiina 希望我的更新和 Adrian 的帮助能够帮你找到答案。 - developer_hatch
@DamianLattenero 这个更新使答案变得更糟。这个问题是关于Java的,而在Java中没有运算符重载。 - Adrian Shum
@AdrianShum 关于“常识”,请先看我在你的回答中留下的第一条评论... - Tiina
@AdrianShum 不,这只是我在lambda中忘记的一个函数部分。我知道变量如何在函数之间传递。 :) - Tiina
显示剩余2条评论

1
记住,lambda表达式是一种表示函数接口(只有一个抽象方法的接口)实现的匿名函数的方式。
在您的iterate()方法中,第三个参数是IntUnaryOperator。它有一个单独的抽象方法applyAsInt(),它以int为参数并返回int。如果将Lambda表达式重写为等效的匿名内部类(在这里您可以合法地使用),则会得到:
IntStream.iterate(0, i -> i < 10, new IntUnaryOperator() {
  @Override
  public int applyAsInt(int i) {
    return i++;
  }
})
.forEach(System.out::println);

在这种情况下,很明显你是在增量(后缀操作符)之前返回i的值。i的初始值为零,因此你将得到无限流的零。如果你将applyAsInt()方法更改为使用前缀操作符++i,则i的值将在返回之前递增,从而给出所需的结果1、2...9。

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