I am trying to turn a List<String>
into a Map<T, String>
, with the value of the map being an element contained within the previous List<String>
, and the key being some attribute of that String
(for example, the length of the String
, in which case, T
would actually be an Integer
).
I first attempted to do it this way. This is an implementation of the example I mentioned above. I want the length of the String
to be the key, and the String
itself to be the value. I wanted to use the Function::identity
function to clearly specify that the value is the String
itself.
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
public class SOQ_ME_20220522_fail
{
public static void main(String[] args)
{
final List<String> list = List.of("apple", "banana", "coconut");
var result =
list.stream()
.collect(
Collectors.toMap(
each -> each.length(),
Function::identity
)
)
;
System.out.println(result);
}
}
However, when I tried to compile it, I got the following compilation error.
SOQ_ME_20220522_fail.java:15: error: no suitable method found for toMap((each)->ea[...]gth(),Function::identity)
Collectors.toMap(
^
method Collectors.<T#1,K#1,U#1>toMap(Function<? super T#1,? extends K#1>,Function<? super T#1,? extends U#1>) is not applicable
(cannot infer type-variable(s) T#1,K#1,U#1
(argument mismatch; unexpected static method <T#2>identity() found in unbound lookup))
method Collectors.<T#3,K#2,U#2>toMap(Function<? super T#3,? extends K#2>,Function<? super T#3,? extends U#2>,BinaryOperator<U#2>) is not applicable
(cannot infer type-variable(s) T#3,K#2,U#2
(actual and formal argument lists differ in length))
method Collectors.<T#4,K#3,U#3,M>toMap(Function<? super T#4,? extends K#3>,Function<? super T#4,? extends U#3>,BinaryOperator<U#3>,Supplier<M>) is not applicable
(cannot infer type-variable(s) T#4,K#3,U#3,M
(actual and formal argument lists differ in length))
where T#1,K#1,U#1,T#2,T#3,K#2,U#2,T#4,K#3,U#3,M are type-variables:
T#1 extends Object declared in method <T#1,K#1,U#1>toMap(Function<? super T#1,? extends K#1>,Function<? super T#1,? extends U#1>)
K#1 extends Object declared in method <T#1,K#1,U#1>toMap(Function<? super T#1,? extends K#1>,Function<? super T#1,? extends U#1>)
U#1 extends Object declared in method <T#1,K#1,U#1>toMap(Function<? super T#1,? extends K#1>,Function<? super T#1,? extends U#1>)
T#2 extends Object declared in method <T#2>identity()
T#3 extends Object declared in method <T#3,K#2,U#2>toMap(Function<? super T#3,? extends K#2>,Function<? super T#3,? extends U#2>,BinaryOperator<U#2>)
K#2 extends Object declared in method <T#3,K#2,U#2>toMap(Function<? super T#3,? extends K#2>,Function<? super T#3,? extends U#2>,BinaryOperator<U#2>)
U#2 extends Object declared in method <T#3,K#2,U#2>toMap(Function<? super T#3,? extends K#2>,Function<? super T#3,? extends U#2>,BinaryOperator<U#2>)
T#4 extends Object declared in method <T#4,K#3,U#3,M>toMap(Function<? super T#4,? extends K#3>,Function<? super T#4,? extends U#3>,BinaryOperator<U#3>,Supplier<M>)
K#3 extends Object declared in method <T#4,K#3,U#3,M>toMap(Function<? super T#4,? extends K#3>,Function<? super T#4,? extends U#3>,BinaryOperator<U#3>,Supplier<M>)
U#3 extends Object declared in method <T#4,K#3,U#3,M>toMap(Function<? super T#4,? extends K#3>,Function<? super T#4,? extends U#3>,BinaryOperator<U#3>,Supplier<M>)
M extends Map<K#3,U#3> declared in method <T#4,K#3,U#3,M>toMap(Function<? super T#4,? extends K#3>,Function<? super T#4,? extends U#3>,BinaryOperator<U#3>,Supplier<M>)
1 error
Now, this is very easy to workaround - I just need to sacrifice some readability by replacing Function::identity
with (t -> t)
. Doing this, the class compiles and runs just fine.
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
public class SOQ_ME_20220522_success
{
public static void main(String[] args)
{
final List<String> list = List.of("apple", "banana", "coconut");
var result =
list.stream()
.collect(
Collectors.toMap(
each -> each.length(),
t -> t
)
)
;
System.out.println(result);
}
}
{5=apple, 6=banana, 7=coconut}
My question is this - why am I unable to use Function::identity
to be able to fetch the data I need? Looking at the documentation for Function::identity
, we can see that this static interface method is supposed to return a Function<T, T>
that returns the object it receives. And if we go to the source code itself, we can see that it creates the exact same lambda that I did. So I am confused why the first attempt failed. Obviously, I am able to get past it easily, but I still want to know why the first attempt failed.
Replace Function::identity
with Function.identity()
.
The former, which is a method reference, would only work if you had a stream of Function
objects and you wanted to call the identity
method on each of them. The latter is simply a static method call which returns a Function
that you pass as the value mapper argument.
Two notes:
each -> each.length()
lambda expression can be written as String::length
(a method reference). Though you of course can keep it as a lambda expression if you want to.Collectors#groupingBy(...)
.