Java8流/收集器与收集器的映射键。计数()

Java8流/收集器与收集器的映射键。计数(),java,java-8,java-stream,Java,Java 8,Java Stream,我想得到由频率键控的数组值频率的映射。 我能够得到相反的地图键的价值。尝试切换参数,但分组依据不接受收集器作为第一个参数 另一个问题是,如何将Map实现更改为LinkedHashMap,以保持按数组元素排序(如果Map由值键控) int[]arr={1,4,5,9,2,4,3,1,1,3,5,3,2,4,2,9,2,1}; Map collect=数组 .stream(arr) .boxed() .collect(Collectors.groupingBy(Function.identity()

我想得到由频率键控的数组值频率的映射。 我能够得到相反的地图键的价值。尝试切换参数,但
分组依据
不接受
收集器
作为第一个参数

另一个问题是,如何将
Map
实现更改为
LinkedHashMap
,以保持按数组元素排序(如果Map由值键控)

int[]arr={1,4,5,9,2,4,3,1,1,3,5,3,2,4,2,9,2,1};
Map collect=数组
.stream(arr)
.boxed()
.collect(Collectors.groupingBy(Function.identity()、Collectors.counting());
结果是
{1=4,2=4,3=3,4=3,5=2,9=2}
我要找的是
{4=1,3=[4,3],2=[5,9]…}
这意味着有4次出现1,3次出现4,2次出现5和9

我的目标是以频率作为键,以值作为数组元素的列表来获取Map。对于键冲突,请在值列表中添加另一个值。

无流解决方案 Guava附带了一个
Multiset
Multimap
实现,您可以像这样使用它来获得相同的行为:

Multiset<Integer> set = HashMultiset.create();
for (int i : array) {
   set.add(i);
}

Multimap<Integer, Integer> byFrequency = HashMultimap.create();
for (Integer key : set.keySet()) {
    byFrequency.put(set.count(key), key);
}

Collection<Integer> allKeysWithFreqOfFive = byFrequency.get(5);
Multiset set=HashMultiset.create();
for(int i:array){
增加(i);
}
Multimap byFrequency=HashMultimap.create();
for(整数键:set.keySet()){
按频率输入(设置计数(键),键);
}
收集allKeysWithFreqOfFive=byFrequency.get(5);

一种可能的解决方案是首先创建映射值->频率,然后将其收集到另一个映射中,在该映射中按频率进行分组

Map<Long, List<Integer>> collect = 
    Arrays.stream(arr)
          .boxed()
          .collect(collectingAndThen(groupingBy(identity(), counting()),
                                     m -> m.entrySet().stream().collect(groupingBy(Map.Entry::getValue, mapping(Map.Entry::getKey, toList())))));

这是一个测试流API的好问题,因为大多数问题都停留在第一个GROUPBY:GROUPBY元素上。这个问题继续第二组,对我来说很合理。以下是我的一些想法。首先,让我们尝试通过过渡for循环来实现它(虽然这不是OP想要的,但我想稍后比较不同的解决方案)

问题是仍然有一些完全不必要的样板代码掩盖了代码的意图。有一个非常有用的Java 8库可以帮助我们减少样板代码:


零样板代码,完美链条。代码告诉您,并且只告诉您想要什么。

示例中的“反向映射”将4映射到1和2。正如您已经知道的,一个键只能映射到一个值,因为如果您再次尝试输入同一个键,该值将被覆盖。所以我不清楚你想要实现什么。试着解释一下你的目标是什么,而不是“如何”实现它(也称为xy问题)。你不能同时拥有
2=5
2=9
。对不起,我已经更正了我的问题。谢谢,但我对Java流解决方案感兴趣。@joe.d是的,这并不容易,因为没有
MultiMap
收集器,因为它在SDK之外,
Collectors.toMap()
中的合并功能不够强大,无法实现您想要的功能。@joe.d AlexisC刚刚击败了我;-)雅致的
收藏,然后
+1.@Alexis C.谢谢!它可以工作,但我们可以保留数组元素的顺序(不是按键排序,而是按数组元素排序)。我想LinkedHashMap会这么做吗?
Map<Long, List<Integer>> collect = 
    Arrays.stream(arr)
          .boxed()
          .collect(collectingAndThen(groupingBy(identity(), counting()),
                                     m -> m.entrySet().stream().collect(groupingBy(Map.Entry::getValue, mapping(Map.Entry::getKey, toList())))));
Map<Long, List<Integer>> result = new HashMap<>();
collect.forEach((value, freq) -> result.computeIfAbsent(freq, u -> new ArrayList<>()).add(value));
Map<Integer, Integer> occursMap = new LinkedHashMap<>(); // to keep the order
for (int i : arr) {
    occursMap.compute(i, (k, v) -> v == null ? 1 : v + 1);
}

Map<Integer, List<Integer>> res = new HashMap<>();
for (Map.Entry<Integer, Integer> entry : occursMap.entrySet()) {
    res.computeIfAbsent(entry.getValue(), k -> new ArrayList<>()).add(entry.getKey());
}
Map<Long, List<Integer>> res = Arrays.stream(arr).boxed()
        .collect(collectingAndThen(groupingBy(Function.identity(), LinkedHashMap::new, counting()),
                m -> m.entrySet().stream().collect(groupingBy(Map.Entry::getValue, mapping(Map.Entry::getKey, toList())))));
res = Arrays.stream(arr).boxed()
        .collect(groupingBy(Function.identity(), LinkedHashMap::new, counting()))
        .entrySet().stream().collect(groupingBy(Map.Entry::getValue, mapping(Map.Entry::getKey, toList())));
res = EntryStream.of(
               IntStreamEx.of(arr).boxed().groupingBy(Function.identity(), () -> new LinkedHashMap<>(), counting())
        ).invert().grouping();
res = StreamEx.of(arr).groupByToEntry(Fn.identity(), Fn.countingInt(), LinkedHashMap::new)
        .inversed().groupTo();