4

I happen to know that, in the following expression, using i++ would result in infinite stream, i would be always 0. I am confused is because I think i++ returned value is not used, even so, it should not interrupt i increment afterwards.

IntStream.iterate(0, i-> i<10, i-> ++i).forEach(....)
Tiina
  • 4,285
  • 7
  • 44
  • 73
  • 2
    I'm not quite familiar with java9, but should'nt `IntStream.iterate(0, i-> i<10, i-> i+1).forEach(....)` be more natural? do you have any reason to use `i++` or `++i` ? – ymonad Aug 02 '17 at 01:29
  • 1
    Possible duplicate of [Java: Prefix/postfix of increment/decrement operators?](https://stackoverflow.com/questions/5413548/java-prefix-postfix-of-increment-decrement-operators) – DarkCygnus Aug 02 '17 at 01:48
  • @ymonad `i+1` also works. I was wondering why `i++` does not. now it is clear. `i++` is ran in a function and the function returned value is assigned to `i`... – Tiina Aug 02 '17 at 01:50

3 Answers3

5

By checking the API of Java 9 IntStream : http://download.java.net/java/jdk9/docs/api/java/util/stream/IntStream.html#iterate-int-java.util.function.IntPredicate-java.util.function.IntUnaryOperator-

The last function (i -> ++i in your case) is to determine what's the next value.

If you put i->i++, given it is a postfix increment operator, i++ evaluates to i before increment. Which means it is always returning the same value (seed 0 in your case). Therefore it works just like you are putting i -> i. Please note that arguments in Java is passed by value. Therefore your increment in the lambda is not going to affect caller.

Therefore, the hasNext predicate (2nd argument, i->i<10 in your case) always evaluates to true, hence giving you an infinite stream of all zeros.

Adrian Shum
  • 38,812
  • 10
  • 83
  • 131
  • There is a very confusing sequence in `i -> i++`. 1) `i` is assigned to `i`, 2) `i` increases by 1. But no matter which comes first, unless `i` is never increased by 1 (like `++` operator always does), `i` should not be 0 forever. This is the part I don't understand. – Tiina Aug 02 '17 at 01:36
  • 1
    the 3rd argument is a function for you to tell `IntStream` what's the next value (given the previous one). It doesn't matter what you do in the function, as long as you returned certain value, that returned value is the next one to be used. – Adrian Shum Aug 02 '17 at 01:38
  • 1
    take it another way, `i -> i++` in your case is just like `i -> {int temp = i; i = i + 1; return temp}`. Given arguments are passed-by-value, your `i=i+1` have no effect on caller. Therefore next value is just always be the same as the original value. – Adrian Shum Aug 02 '17 at 01:39
  • ah, I seeeeeeee – Tiina Aug 02 '17 at 01:41
1

You can use limit and do what you propose:

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

Also this could help:

Java Incremental operator query (++i and i++)

developer_hatch
  • 15,898
  • 3
  • 42
  • 75
  • But still wonder why `i++` does not increase `i`. This challenges a "common sense" to me. – Tiina Aug 02 '17 at 01:28
  • @Tina Arguments in Java is passed by value. I believe you haven't developed the common sense in Java yet. – Adrian Shum Aug 02 '17 at 01:30
  • @Tiina hope with my update and adrian help you culd fin your answer – developer_hatch Aug 02 '17 at 01:34
  • @DamianLattenero The update is making the answer worse. This question is about Java, and there is no operator overloading in Java. – Adrian Shum Aug 02 '17 at 01:35
  • @AdrianShum about the "common sense" please see first comment I left in your answer... – Tiina Aug 02 '17 at 01:39
  • @Tina That's why I said : "Common Sense in Java". Computer languages works in a very strict way. A similar-looking statement may behave differently in different languages. – Adrian Shum Aug 02 '17 at 01:42
  • @AdrianShum no it's just a function part in lambda that I forgot. I know how variables are passed between functions. :) – Tiina Aug 02 '17 at 01:46
1

Remember, a lambda expression is a way of representing, as an anonymous function, the implementation of a functional interface (an interface that has only one abstract method).

In your iterate() method, the third argument is an IntUnaryOperator. This has a single abstract method, applyAsInt(), which takes an int as an argument and returns an int. If you re-write the Lambda expression as the equivalent anonymous inner class (which you can quite legitimately use here), you get:

IntStream.iterate(0, i -> i < 10, new IntUnaryOperator() {
  @Override
  public int applyAsInt(int i) {
    return i++;
  }
})
.forEach(System.out::println);

In this case, it is clear that you are returning the value of i before it is incremented (postfix operator). The initial value of i is zero so you will get an infinite stream of zeros. If you change the applyAsInt() method to use the prefix operator, ++i, the value of i will be incremented before being returned giving you the desired result of 1, 2 ... 9

Speakjava
  • 3,177
  • 13
  • 15