Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid "incompatible parameter types in lambda expression" when adding to an ArrayList?

I have the following code

public static List<Integer> topKFrequent(int[] nums, int k) {
  List<Integer> myList = new ArrayList<>();
  HashMap<Integer, Integer> map = new HashMap<>();

  for (int n : nums) {
    if (!map.containsKey(n)) map.put(n, 1);
    else map.put(n, map.get(n) + 1);
  }

  map.entrySet().stream()
    .sorted(Map.Entry.<Integer, Integer>comparingByValue().reversed())
    .limit(k)
    .forEach((key, value) -> myList.add(key));

  return myList;
}

The forEach throws the error

Error:(20, 16) java: incompatible types: incompatible parameter types in lambda expression

How can I fix/avoid this error? I'm not quite sure how to apply the answer here that explains the problem: Lambda Expression and generic method

Edit:

Given the answer, the correction is to replace the lambda inside the forEach with

.forEach((entry) -> myList.add(entry.getKey()));
like image 814
m0meni Avatar asked May 09 '16 22:05

m0meni


People also ask

Is it mandatory to declare type of parameter in lambda expression?

Optional type declaration − No need to declare the type of a parameter. The compiler can inference the same from the value of the parameter. Optional parenthesis around parameter − No need to declare a single parameter in parenthesis. For multiple parameters, parentheses are required.

Is parameter types can be inferred in lambda expression?

Type Inference means that the data type of any expression (e.g. method return type or parameter type) can be deduced automatically by the compiler. Groovy language is a good example of programming languages supporting Type Inference. Similarly, Java 8 Lambda expressions also support Type inference.

Can we write parameters less lambda expression?

No, there isn't. Lambda expressions are optimised (in terms of syntax) for the single parameter case. I know that the C# team feels your pain, and have tried to find an alternative.

How do you pass a lambda function as a parameter in Java?

Basically to pass a lamda expression as a parameter, we need a type in which we can hold it. Just as an integer value we hold in primitive int or Integer class. Java doesn't have a separate type for lamda expression instead it uses an interface as the type to hold the argument.


2 Answers

entrySet() returns a set of Pair<K, V>.

forEach()'s lambda therefore takes a single parameter of that type; not two integer parameters.

like image 125
SLaks Avatar answered Sep 27 '22 22:09

SLaks


You are going about it in a java7-ish way. Modifying external data structures from inside forEach is not how Streams API was meant to be used. Streams API documentation specifically warns against such use in the Side-Effects section of java.util.stream package summary

Instead of appending to list or map from inside forEach, use collect:

import static java.util.Comparator.reverseOrder;
import static java.util.Map.Entry.comparingByValue;
import static java.util.stream.Collectors.counting;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toList;


public static List<Integer> topKFrequent(int[] nums, int k) {
    Map<Integer, Long> freq = Arrays.stream(nums).boxed()
            .collect(groupingBy(x->x, counting()));

    return freq.entrySet()
            .stream()
            .sorted(comparingByValue(reverseOrder()))
            .limit(k)
            .map(Map.Entry::getKey)
            .collect(toList());
}
like image 44
Misha Avatar answered Sep 28 '22 00:09

Misha