为了账号安全,请及时绑定邮箱和手机立即绑定

Map<K,V> 在按值分组后返回到 Map<V,Map<K,V>>

Map<K,V> 在按值分组后返回到 Map<V,Map<K,V>>

慕无忌1623718 2022-12-15 10:47:47
我正在努力维护我想要的跨 Java 流操作的数据结构,这很可能是由于缺乏适当的理解和实践。public class Main {    public static void main(String[] args) {        List<Integer> list = Arrays.asList(1, 1, 1, 2, 3, 3, 3, 3);            //Group by            Map <Integer, Long> countGrouped = list.stream().collect(                    Collectors.groupingBy(                            x -> x, Collectors.counting()));            System.out.println("group by value, count " + countGrouped);            //Sort desc            Map <Integer, Long> descendingSorted = new LinkedHashMap<>();            countGrouped.entrySet().stream()                .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))                .forEachOrdered(x -> descendingSorted.put(x.getKey(), x.getValue()));            System.out.println("sorted " + descendingSorted);            //filter            Map <Integer, Long> filtered = new LinkedHashMap<>();            descendingSorted.entrySet().stream()                .filter(x -> x.getValue() >= 2)                .forEach(x -> filtered.put(x.getKey(), x.getValue()));;            System.out.println("filtered " + filtered);            //Split groups            Map<Object, List<Entry<Integer, Long>>> groups = filtered.entrySet().stream()                    .collect(Collectors.groupingBy(x -> x.getValue()));            System.out.println("grouped " + groups);    }}导致group by value, count {1=3, 2=1, 3=4}sorted {3=4, 1=3, 2=1}filtered {3=4, 1=3}grouped {3=[1=3], 4=[3=4]}这是正确的,但正如您所看到的,我正在逐渐进入更深奥的数据结构,没有特别的意义,如您所见,以(wtf?)结束Map<Object, List<Entry<Integer, Long>>>。虽然它可以只是一个Map<Int, Map<Int, Int>>.所以具体问题是,如何转换和包含流操作产生的数据结构输出?我已经看到 Collectors 提供了对 Map(...) 的转换操作,我想这是要走的路,但我无法(我认为是由于缺乏适当的知识)让它工作。在这种情况下,在我看来,教学解释、链接到综合资源以更好地理解流和函数式编程或类似的东西,比针对特定情况的实际解决方案更有帮助(这对一个练习,但你明白了)
查看完整描述

2 回答

?
www说

TA贡献1775条经验 获得超8个赞

你在这里遇到困难有点令人惊讶,因为你已经展示了所有必要事物的知识。您知道groupingBy可以取另一个Collector,您toMap已经命名了正确的,并且您已经使用函数来提取Map.Entry值。


结合这些东西,给你


Map<Long, Map<Integer, Long>> groups = filtered.entrySet().stream()

    .collect(Collectors.groupingBy(x -> x.getValue(),

        Collectors.toMap(x -> x.getKey(), x -> x.getValue())));

System.out.println("grouped " + groups);

为了更好地演示操作,我将输入更改为


List<Integer> list = Arrays.asList(1, 1, 1, 2, 3, 3, 3, 3, 4, 4, 4);

这导致


grouped {3=[1=3, 4=3], 4=[3=4]}

但是,重复始终与外部映射键相同的计数是没有意义的。所以另一种选择是


Map<Long, List<Integer>> groups = filtered.entrySet().stream()

    .collect(Collectors.groupingBy(Map.Entry::getValue,

        Collectors.mapping(Map.Entry::getKey, Collectors.toList())));

System.out.println("grouped " + groups);

这导致


grouped {3=[1, 4], 4=[3]}

请注意,您不应将forEach/forEachOrdered用于put映射。您的中间步骤应该是


//Sort desc

Map<Integer, Long> descendingSorted = countGrouped.entrySet().stream()

    .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))

    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,

        (a,b) -> { throw new AssertionError(); }, LinkedHashMap::new));

System.out.println("sorted " + descendingSorted);


//filter

Map<Integer, Long> filtered = descendingSorted.entrySet().stream()

    .filter(x -> x.getValue() >= 2)

    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,

        (a,b) -> { throw new AssertionError(); }, LinkedHashMap::new));

System.out.println("filtered " + filtered);

接受地图工厂的toMap收集器迫使我们提供合并功能,但由于我们的输入已经是一个必须具有不同键的地图,我在这里提供了一个始终抛出的功能,因为如果出现重复项,就会出现严重错误。


但请注意,强制所有这些操作收集到新地图中是不必要的复杂和低效的。首先对整个数据进行排序并filter随后减少数据量也没有意义。首先过滤可能会减少排序步骤的工作,而过滤操作的结果不应取决于顺序。


在单个管道中完成整个操作要好得多


List<Integer> list = Arrays.asList(1, 1, 1, 2, 3, 3, 3, 3, 4, 4, 4);


Map<Integer, Long> countGrouped = list.stream().collect(

    Collectors.groupingBy(x -> x, Collectors.counting()));

System.out.println("group by value, count " + countGrouped);


Map<Long, List<Integer>> groups = countGrouped.entrySet().stream()

    .filter(x -> x.getValue() >= 2)

    .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))

    .collect(Collectors.groupingBy(Map.Entry::getValue, LinkedHashMap::new, 

        Collectors.mapping(Map.Entry::getKey, Collectors.toList())));


System.out.println("grouped " + groups);

请注意,与之前的代码不同,现在最后的分组操作也会保留顺序,从而导致


grouped {4=[3], 3=[1, 4]}

即,组按计数降序排序。


由于计数是结果映射的键,我们还可以使用本质上排序的映射作为结果类型并省略排序步骤:


Map<Long, List<Integer>> groups = countGrouped.entrySet().stream()

    .filter(x -> x.getValue() >= 2)

    .collect(Collectors.groupingBy(Map.Entry::getValue,

        () -> new TreeMap<>(Comparator.<Long>reverseOrder()),

        Collectors.mapping(Map.Entry::getKey, Collectors.toList())));

主要区别在于流操作后结果映射的行为,例如,如果您向其中插入更多元素,因为TreeMap将根据降序插入新键,而LinkedHashMap将它们追加到末尾,保持插入顺序。


查看完整回答
反对 回复 2022-12-15
?
神不在的星期二

TA贡献1963条经验 获得超6个赞

的签名groupingBy是public static <T, K> Collector<T, ?, Map<K, List<T>>>

    groupingBy(Function<? super T, ? extends K> classifier),但如果我理解正确,您只想将值映射到地图条目,例如:


Map<Object, Map.Entry<Integer, Long>> groups = filtered.entrySet().stream()

        .collect(Collectors.toMap(Map.Entry::getValue, x -> x));

System.out.println("grouped " + groups);

输出


grouped {3=1=3, 4=3=4}


查看完整回答
反对 回复 2022-12-15
  • 2 回答
  • 0 关注
  • 113 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信