gpt4 book ai didi

java - Map 在按值分组后返回到 Map>,而不是 Map>>

转载 作者:塔克拉玛干 更新时间:2023-11-02 08:00:05 27 4
gpt4 key购买 nike

我在 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(...) 提供转换操作,我想这是要走的路,但我无法(我认为是由于缺乏适当的知识)让它工作。

在这种情况下,在我看来,教学解释、链接到综合资源以更好地理解流和函数式编程,或类似的东西,比针对特定情况的实际解决方案(这将很适合锻炼,但你明白了)

最佳答案

你在这里遇到困难有点令人惊讶,因为你已经展示了所有必要事物的知识。您知道 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放入 到 map 中。您的中间步骤应该是

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

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

但请注意,强制将所有这些操作收集到新 map 中是不必要的复杂和低效的。也没有必要首先对整个数据进行排序,然后通过 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 会将它们附加到末尾,保持插入顺序。

关于java - Map<K,V> 在按值分组后返回到 Map<V,Map<K,V>>,而不是 Map<Obj, List<Entry<K,V>>>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56288198/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com