3
final Stream<Integer> numbers = Stream.of(5, 3, 2, 7, 3, 13, 7).parallel();

Why the output of the following line is 7?

 numbers.reduce(1, (a, b) -> a + b, (x, y) -> x - y)); 
Misha
  • 27,433
  • 6
  • 62
  • 78
  • 2
    You are violating the contract of `reduce` as `1` is not an identity for your accumulator/combiner. [See javadoc](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#reduce-U-java.util.function.BiFunction-java.util.function.BinaryOperator-) – Misha Sep 24 '17 at 03:23
  • Why it is not an identity? See this code - http://www.concretepage.com/java/jdk-8/java-8-stream-reduce-example (ReduceDemo3) – Chlebik Sep 24 '17 at 07:38
  • 1
    that link has some problems also, as it violates some properties; please read the documentation and not that – Eugene Sep 24 '17 at 08:24
  • 2
    Anyone can set up a web page. And there are tons of them, not telling you more than the [official tutorial](http://docs.oracle.com/javase/tutorial/collections/streams/index.html) or the [API documentation](http://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#package.description). But that page you’ve linked, is one of the worst, just being horribly wrong. Perhaps [this Q&A](https://stackoverflow.com/questions/32866581/in-stream-reduce-method-must-the-identity-always-be-0-for-sum-and-1-for-multipl?rq=1) helps… – Holger Sep 24 '17 at 14:07

1 Answers1

4

I have not looked at that link from the comments, but the documentation is pretty clear about identity and it even provides a simple way of testing that:

The identity value must be an identity for the combiner function. This means that for all u, combiner(identity, u) is equal to u

So let's simplify your example a bit:

Stream<Integer> numbers = Stream.of(3, 1).parallel();
BiFunction<Integer, Integer, Integer> accumulator = (a, b) -> a + b;

BiFunction<Integer, Integer, Integer> combiner = (x, y) -> x - y; 

    int result = numbers.reduce(
            1,
            accumulator,
            combiner);

    System.out.println(result);

let's say that u = 3 (just a random element from the Stream), thus:

    int identity = 1;
    int u = 3;

    int toTest = combiner.apply(identity, u);
    System.out.println(toTest == identity); // must be true, but is false

Even if you think that you would replace identity with zero, that would work; the documentation makes another argument:

Additionally, combiner function must be compatible with the accumulator function; for all u and t, the following must hold:

 combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t)

You can make the same test:

int identity = 0;
int u = 3;
int t = 1;

boolean associativityRespected = 
     combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t);
System.out.println(associativityRespected); // prints false
Eugene
  • 117,005
  • 15
  • 201
  • 306
  • 2
    Or in other words, doing two entirely different things in `reduce`, like `(a,b)->a+b` and `(x,y)->x-y`, is nonsense. Further, `(x, y) -> x - y` is already violating the contract on its own, as it’s not associative. It’s impressive, how many wrong things the author of that web page managed to put into *one example*… – Holger Sep 25 '17 at 07:04
  • @Holger all I can say that being in the position to port some code to java-8 (and managers being completely ignorant that *it does require* quiet a lot of time to understand these things), finding a good resource online is really hard. So, yes, I can't agree more. Good thing u are around, obviously. – Eugene Sep 25 '17 at 07:20
  • In you first test, I guess you meant `System.out.println(toTest == u);` – Thiyagu Dec 15 '19 at 13:07