Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why Collectors.groupingBy gets confused with identity function?

I'm trying to count occurrences of integers in an array using Collectors.groupingBy from Java 8 API but I'm getting some strange compilation errors.

Here is my code:

List<Integer> l = Arrays.asList(1, 1, 1, 2, 3, 3, 3, 3);
Map<Integer, Integer> x = l.stream().collect(groupingBy(i -> i, counting()));

Sadly, this won't compile, resulting in the following error:

error: incompatible types: inferred type does not conform to equality constraint(s)
        Map<Integer, Integer> x = l.stream().collect(groupingBy(i -> i, counting()));
                                                    ^
    inferred: Integer
    equality constraints(s): Integer,Long
1 error

It seems to be a generic type issue, because when I remove the generic Map type, it compiles. Here is another test:

List<Integer> l = Arrays.asList(1, 1, 1, 2, 3, 3, 3, 3);
Map x = l.stream().collect(groupingBy(i -> i, counting()));

System.out.println(x);

And the output is right as expected:

{1=3, 2=1, 3=4}

Any ideas of how to solve that without the need of casting all the types here and there?

like image 919
Chico Sokol Avatar asked Aug 11 '15 13:08

Chico Sokol


2 Answers

If you do:

Map<Integer, Long> x = l.stream().collect(Collectors.groupingBy(i -> i, Collectors.counting()));

then your code will compile just fine.

The reason is that the Collectors.counting() method is defined as:

public static <T> Collector<T, ?, Long> counting()

Here, the third type-parameter denotes the type which will be used in the BinaryOperator that is used for calculating the count.

Note that when you remove the type-parameter for the Map:

Map x = l.stream().collect(groupingBy(i -> i, counting()));

then the statement successfully compiles, because you're actually working with the raw version of Map, i.e. the type for both keys and values will be Object (which is compatible with Long, Integer and friends). However, using raw types is something that should be avoided, as you can get annoying ClassCastException(s) at Runtime.

like image 185
Konstantin Yovkov Avatar answered Nov 05 '22 05:11

Konstantin Yovkov


counting() is declared as:

static <T> Collector<T,?,Long>

... whereas you're trying to use it as if it produces an Integer.

If you change your code to:

Map<Integer, Long> x = l.stream().collect(groupingBy(i -> i, counting()));

... it will compile with no problems. Note that in your current code using the raw type, your output actually has Long values rather than Integer values... it's just you can't tell that from the string representation.

like image 27
Jon Skeet Avatar answered Nov 05 '22 04:11

Jon Skeet