反转 Collectors.toMap 以添加到 ArrayList

Inverted Collectors.toMap to add to an ArrayList

我想将数字的频率放在 TreeMap 中,以频率作为键,将具有该频率的数字放在 ArrayList 中。

我有两个问题:

1) 我在第一个参数中收到 "non-static methods cannot be referenced from a static context" 错误(据我所知,流引用了一个对象 - 发生了什么事?)

2) Collectors.toMap() 有 4 个参数 - 似乎参数 4 需要使用新的 TreeMap> 进行初始化,参数 2 可以是 ArrayList add() 函数,参数 3 可以为 null (可能是)。这是怎么做到的?

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, ...));

目前我无法使用查看如何从 https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html 到我需要去的地方。

您的推理几乎是正确的...只是第三个参数是您合并同一个键的值的地方 - 所以您不能省略它。

 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));

我认为 Collectors.groupingByCollectors.toMap 更能满足您的需求:

Map<Long, List<Integer>> tm = 
    m.entrySet()
     .stream()
     .collect(Collectors.groupingBy(Map.Entry::getValue, // group the entries by the 
                                                         // value (the frequency)
                                    TreeMap::new, // generate a TreeMap
                                    Collectors.mapping (Map.Entry::getKey, 
                                                        Collectors.toList()))); // the
                                                         // value of the output TreeMap 
                                                         // should be a List of the 
                                                         // original keys

您可以将 Collectors.toList() 替换为 Collectors.toCollection(ArrayList::new) 以确保输出 Map 的值是 ArrayLists(尽管 [=18= 的当前实现] 已经导致 java.util.ArrayList 个实例)。

对于您的样本输入,这会产生以下 TreeMap

{1=[1, 3, 5, 6, 8], 2=[2], 3=[4]}

我不会使用流来创建倒置地图。相反,我会这样做:

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]}

由于创建倒置图的代码很短,您可以使用Collectors.collectingAndThen一步创建频率和倒置图:

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;
        }));