Why does the Java mapFactory argument of the method toMap in the Collectors class need the types to be specified?

Viewed 268

The following code does not compile with my JDK14:

Map<Integer, String> map = Arrays.asList("this", "is", "just", "an", "example").stream()
    .collect(Collectors.toMap(w -> w.length(),
                              w -> w,
                            (existing, replacement) -> replacement,
                            () -> new TreeMap<>(Comparator.reverseOrder())));

while if I specify the types for the constructor of the TreeMap it works fine:

Map<Integer, String> map4 = Arrays.asList("this", "is", "just", "an", "example").stream()
    .collect(Collectors.toMap(w -> w.length(),
                              w -> w,
                            (existing, replacement) -> replacement,
                            () -> new TreeMap<Integer, String>(Comparator.reverseOrder())));

Am I missing something or is there a bug in the type erasure system of JDK14?

1 Answers

It seems there is an issue with inferring type for Comparator in case of chained calls.

It could work if you create a map supplier beforehand (btw, not only in JDK14):

Supplier<Map<Integer, String>> supplier = () -> new TreeMap<>(Comparator.reverseOrder());

Map<Integer, String> mapWithSupplier = Arrays.asList("this", "is", "just", "an", "example").stream()
                .collect(Collectors.toMap(w -> w.length(),
                        w -> w,
                        (existing, replacement) -> replacement,
                        supplier));

If you used Collections.reverseOrder(), you would not encounter this issue at all:

Map<Integer, String> mapCollectionReversed = Arrays.asList("this", "is", "just", "an", "example").stream()
                .collect(Collectors.toMap(w -> w.length(),
                        w -> w,
                        (existing, replacement) -> replacement,
                        () -> new TreeMap<>(Collections.reverseOrder())));

or at least created a comparator beforehand:

Comparator<Integer> comparator = Comparator.reverseOrder();

Map<Integer, String> mapWithComparator = Arrays.asList("this", "is", "just", "an", "example").stream()
                .collect(Collectors.toMap(w -> w.length(),
                        w -> w,
                        (existing, replacement) -> replacement,
                        () -> new TreeMap<>(comparator)));

And without reverseOrder the map could be created successfully with TreeMap::new:

Map<Integer, String> map = Arrays.asList("this", "is", "just", "an", "example").stream()
                .collect(Collectors.toMap(w -> w.length(),
                        w -> w,
                        (existing, replacement) -> replacement,
                        TreeMap::new));
Related