5

I have written simple List with example values, and I wanted the stream to return max value from Stream. I know max() function takes Comparator but it turned out, I can pass also Integer::max (anyone can explain my, why?).

Moreover, program prints weird result, I inspected it "inside" and it looks OK, but after I get final result - they are not accurate.

Example:

@Test
public void testHowIntegerMaxWorksInStream() {
    List<Integer> list = Arrays.asList(5,3,8);
    Optional<Integer> op = list.stream().max((a, b) -> {
        System.out.println("Input arguments a=" + a + ", b=" + b);

        int max = Integer.max(a, b);
        System.out.println("Returning max(a,b)=" + max);
        return max;
    });
    System.out.println("Optional result=" + op.get());
}

Output:

Input arguments a=5, b=3
Returning max(a,b)=5
Input arguments a=5, b=8
Returning max(a,b)=8 // OK, Integer::max got 8.. but then ...
Optional result=5 // .. I got 5. WHY ???

My questions:

  1. Why can I pass Integer::max in place of Comparator ?
  2. Why my function returns 5 as a result while inside it's 8 ?
Tunaki
  • 132,869
  • 46
  • 340
  • 423
mlewandowski
  • 802
  • 7
  • 14
  • 2
    You should be using `Integer::conmpareTo`, not `Integer::max`... – assylias Mar 17 '16 at 12:32
  • 2
    Alternatively you could use `reduce(Integer::max);` since taking the maximum is an associative operation, but this is pretty much the same as `max(Integer::compare)` – ctst Mar 17 '16 at 12:40
  • 1
    The answer to 1): That's how method references work. If the signature of the method is suitable, you can do it; There's no "intelligent check" that prevents this. The answer to 2): If you tell the `max` method that `5>3>8` and `5>8` this is what you get... – fabian Mar 17 '16 at 12:43

3 Answers3

8

You're misunderstanding things. max(comparator) takes a comparator that compares integer. What you're doing is not comparing integers, i.e. telling if a is greater than b or not. You're taking the max of them and returning it. So you're saying that a is always greater than b, since you're always returning a positive number (your list only consists of positive numbers). A Comparator returns:

a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.

What you need to do is

public void testHowIntegerMaxWorksInStream() {
    List<Integer> list = Arrays.asList(5,3,8);
    Optional<Integer> op = list.stream().max((a, b) -> {
        int compare = Integer.compare(a, b);
        return compare;
    });
    System.out.println("Optional result=" + op.get());
}

i.e. calling Integer.compare instead of max, which does exactly that:

Compares its two arguments for order. Returns a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.

For the second part of your question, refer to :: (double colon) operator in Java 8. It is called a method-refence. So another way to write it is:

List<Integer> list = Arrays.asList(5,3,8);
Optional<Integer> op = list.stream().max(Integer::compare);
System.out.println("Optional result=" + op.get());
Community
  • 1
  • 1
Tunaki
  • 132,869
  • 46
  • 340
  • 423
5

The reason that Integer.max, unfortunately, compiles in place of a comparator is that its return type fits what is expected by stream's max method: the result of a comparison is an int, and the result of comparing two ints is an int as well. However, the result returned by Integer.max is inconsistent with what stream's max expects, which explains incorrect result: for example, if you pass (5, 8) to Integer.max it returns 8, a positive number; stream's max, however, interprets all positive numbers as an indication that the first parameter is greater than the second, which is incorrect in this situation.

The only reason why this compiles is that your comparator returns an int. This wouldn't work with Double or BigInteger.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
1

Because you create Comparator that always returns the first element to be the greatest.

If you compare A and B, you should return -1 if A is smaller. You return the Integer.max of A and B, which, in your case, is always >0.

(a,b) -> { 
   if (a>b) return 1;     // <-- you return a positive value always, so stream.max() thinks 5>8 :)
   if (a<b) return -1;
   if (a==b) return 0;
}
Rob Audenaerde
  • 19,195
  • 10
  • 76
  • 121
  • Ahhh! I see! To sum it up: Output of my max function should return `-1`, `0` or `1` and what I have here is to ALWAYS return positive number. Therefore first argument, here calling `a` always wins. Am I right? – mlewandowski Mar 17 '16 at 12:40
  • @mlewandowski yes. You can also experiment with negative values in your list to see what happens ;) – Rob Audenaerde Mar 17 '16 at 12:43
  • The output doesn’t have to be strictly `-1` or `+1`. It can be any integer value but what matters is the meaning of the result being negative, zero or positive. – Holger Mar 17 '16 at 15:13
  • @Holger yes that is correct, thanks for adding the comment . I just simplified it for this example. – Rob Audenaerde Mar 17 '16 at 18:28