Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inverted Collectors.toMap to add to an ArrayList

I would like to put the frequencies of the numbers in a TreeMap with the frequencies as the keys and the numbers that have that frequency in an ArrayList.

I have two problems:

1) I'm getting a "non-static methods cannot be referenced from a static context" error in the first parameter (AFAIK the stream references an object - what's going on?)

2) There are 4 parameters for Collectors.toMap() - it seems like parameter 4 requires initialization with a new TreeMap>, parameter 2 could be an ArrayList add() function and parameter 3 could be null (maybe). How is this done?

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

public class Main {

    public static void main(String[] args) {

        List<Integer> array = Arrays.asList(1, 2, 4, 5, 6, 4, 8, 4, 2, 3);

        Map<Integer, Long> m = array.stream()
            .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

        System.out.println(m);

        TreeMap<Long, List<Integer>> tm = m.entrySet().stream()
            .collect(Collectors.toMap(Map.Entry::getValue, ...));

At the moment I can't use see how to get from https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html to where I need to be.

like image 635
John Estess Avatar asked Jul 05 '17 18:07

John Estess


2 Answers

I wouldn't use streams to create the inverted map. Instead, I would just do:

Map<Long, List<Integer>> tm = new TreeMap<>();
m.forEach((num, freq) -> tm.computeIfAbsent(freq, k -> new ArrayList<>()).add(num));

System.out.println(tm); // {1=[1, 3, 5, 6, 8], 2=[2], 3=[4]}

As the code to create the inverted map is short, you could use Collectors.collectingAndThen to create both the frequencies and inverted map in one step:

TreeMap<Long, List<Integer>> invertedFrequenciesMap = array.stream()
    .collect(Collectors.collectingAndThen(
        Collectors.groupingBy(Function.identity(), Collectors.counting()),
        map -> {
            TreeMap<Long, List<Integer>> tm = new TreeMap<>();
            map.forEach((num, freq) ->
                    tm.computeIfAbsent(freq, k -> new ArrayList<>()).add(num));
            return tm;
        }));
like image 171
fps Avatar answered Oct 16 '22 22:10

fps


You are almost correct in your reasoning... Just that the 3-rd argument is where you merge your values for the same key - so you can't omit it.

 TreeMap<Long, List<Integer>> tm = m.entrySet().stream()
            .collect(Collectors.toMap(
                  Entry::getValue, 
                  x -> {
                      List<Integer> list = new ArrayList<>();
                      list.add(x.getKey());
                      return list;
                  }, 
                  (left, right) -> {
                     left.addAll(right);
                     return left;
                  }, 
                  TreeMap::new));
like image 6
Eugene Avatar answered Oct 16 '22 22:10

Eugene