4

While initializing collections like TreeMap, TreeSet, etc. We can add our custom comparator. The code looks something like:

Map<Integer, String> map1 = new TreeMap<>(new Comparator<Integer>() {
    public int compare(Integer x, Integer y) {
        return x-y;
    }
});

Now, we can make replace this anonymous implementation with a lambda expression. The code would look like:

Map<Integer, String> map2 = new TreeMap<>((x,y) -> x-y);

Java-8 allows you to store lambda expressions in a variable through functional interfaces. So, I modified the above code to the following:

BiFunction<Integer, Integer, Integer> myComparator = (x,y) -> x-y;
Map<Integer, String> map3 = new TreeMap<>(myComparator);

But this last attempt did not work! It gave the following error:

Cannot infer type arguments for TreeMap<>

Why did it fail to resolve types in the last example?

Note: To confirm that this is not an IDE bug, I performed a raw compile using javac and it still gave the same error.

Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183
YetAnotherBot
  • 1,937
  • 2
  • 25
  • 32
  • 1
    Couldn't you declare your lambda function as `Comparator myComparator = (x,y) -> x-y;` instead of `BiFunction`? Since `Comparator` is explicitly the type the `TreeMap` constructor requires. – khelwood Apr 12 '19 at 14:25
  • I get a *"cannot resolve constructor ...."*. – luk2302 Apr 12 '19 at 14:27
  • @luk2302, which version of java are you using? – YetAnotherBot Apr 12 '19 at 14:44
  • @khelwood that works!!. But unfortunately does not resolve the question. – YetAnotherBot Apr 12 '19 at 14:45
  • @AdityaGupta 1.8.0_131 - "no suitable constructor found for ..." is the actual output, the other one is generated by IntelliJ it seems. – luk2302 Apr 12 '19 at 14:46
  • Do you know what a “functional interface” is? It is *not* an interface required to be in the package `java.util.function`. `Comparator` is already a functional interface. That’s why you can use a lambda expression where a `Comparator` is required, like with `new TreeMap<>((x,y) -> x-y)`. What do you hope to gain from using `BiFunction` instead of `Comparator`? Besides that, *don’t use minus as comparator function*. Minus can overflow. And anyway, `Comparator.naturalOrder()` is already suitable to this task. (Or just create the `TreeMap` without a comparator) – Holger Apr 12 '19 at 14:46
  • @luk2302 Try [this](https://gist.github.com/adityarar/58a45a92a08ac81bdadd2d87de730e01) – YetAnotherBot Apr 12 '19 at 14:48
  • @Holger, I was testing out lambda expressions which could be used as comparators at runtime. `Comparator.naturalOrder()` is good advice, but not all implementations will be available in `Comparator`. – YetAnotherBot Apr 12 '19 at 14:51
  • 1
    Regardless of which logic you implement, stay away from using minus. There is `Integer.compare(int,int)` doing the right thing, but usually, when you end up comparing two `int` values, you can use either `Comparator.naturalOrder()`, `Comparator.reverseOrder​()`, `Comparator.comparingInt​(…)`, or a combination of them. – Holger Apr 12 '19 at 14:55

3 Answers3

8

Although the lambda expression seems the same, the BiFunction is not the Comparator therefore you cannot interchange them.

Comparator<Integer> comparator = (x,y) -> x - y;
Map<Integer, String> map3 = new TreeMap<>(comparator);

Let's take a look deeper into these interfaces and implement them using an anonymous class:

Comparator<Integer> comparator = new Comparator<Integer>() {
    @Override
    public int compare(Integer x, Integer y) {
        return x - y;
    }
};

BiFunction<Integer, Integer, Integer> biFun = new BiFunction<Integer, Integer, Integer>() {
    @Override
    public Integer apply(final Integer x, final Integer y) {
        return x - y;
    }
};

The difference is also the name of the method. TreeMap expects Comparator in its constructor because its internal implementation will call compare according to the contract with Comparator.

By the way, the BinaryOperator<T> results in the same lambda expression as well.

Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183
1

That is simply because the TreeMap constructor expects a Comparator<Integer> rather than a BiFunction.

With writing BiFunction<Integer, Integer, Integer> myComparator = ... you explicitly say to the compiler that the variable is of that type. This prevents the compiler from infering the type required to the TreeMap constructor. Since BiFunction is not a Comparator, nor a subclass of it, the compiler does not allow this.

MC Emperor
  • 22,334
  • 15
  • 80
  • 130
0

Because It expects the Comparator and you have supplied the BiFunction. From Oracle Docs :

@FunctionalInterface
public interface Comparator<T>

A comparison function, which imposes a total ordering on some collection of objects. Comparators can be passed to a sort method (such as Collections.sort or Arrays.sort) to allow precise control over the sort order. Comparators can also be used to control the order of certain data structures (such as sorted sets or sorted maps), or to provide an ordering for collections of objects that don't have a natural ordering.

Anant Goswami
  • 318
  • 3
  • 14