英文:
Create a map of maps with counts from list
问题
给定一个 List<Integer> l
和一个因子 int f
,我想要使用流来创建一个 Map<Integer, Map<Integer, Long>> m
,其中父映射的键是在 l
中索引除以 f 得到的,值是一个将值映射到计数的映射。
如果列表是 {1,1,1,4}
,而因子是 f=2
,我想要得到:
0 ->
{
1 -> 2
}
1 ->
{
1 -> 1
4 -> 1
}
基本上,我希望得到一个流版本的以下操作:
Map<Integer, Map<Integer, Long>> m = new HashMap<>();
for (int i = 0; i < l.size(); i++) {
m.computeIfAbsent(i/f, k -> new HashMap<>())
.compute(l.get(i), (k, v) -> v==null?1:v+1);
}
我意识到这与关于收集嵌套映射的问题非常相似,我了解如何执行更简单的 groupingBy
操作来计数:
Map<Integer, Long> m = l.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
但我不知道如何将这两个想法结合起来而不进行迭代。
由于我使用索引作为其中一个键,我想,与其从 l.stream()
开始,不如从 IntStream.range(0, l.size()).boxed()
开始,这让我可以得到第一个键(i -> i/f
)和第二个键(i -> l.get(i)
),但我仍然不知道如何正确地收集计数。
英文:
Given a List<Integer> l
and a factor int f
, I would like to use a stream to create a Map<Integer, Map<Integer, Long>> m
such that the parent map has keys that are the index within l
divided by f, and the value is a map of values to counts.
If the list is {1,1,1,4}
and the factor is f=2
I would like to get:
0 ->
{
1 -> 2
}
1 ->
{
1 -> 1
4 -> 1
}
Basically, I'm hoping for a stream version of:
Map<Integer, Map<Integer, Long>> m = new HashMap<>();
for (int i = 0; i < l.size(); i++) {
m.computeIfAbsent(i/f, k -> new HashMap<>())
.compute(l.get(i), (k, v) -> v==null?1:v+1);
}
I realize it is fairly similar to this question about collecting a map of maps and I understand how to do a much simpler groupingBy
with a count:
Map<Integer, Long> m = l.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
But I do not understand how to put those two ideas together without iterating.
Because I am working with indexes as one of the keys, I imagine that rather than starting with l.stream()
I will start with IntStream.range(0, l.size()).boxed()
which lets me get the first key (i -> i/f
) and the second key(i -> l.get(i)
), but I still don't know how to properly collect the counts.
答案1
得分: 2
以下是翻译好的代码部分:
这是一个解决方案。
public static void main(String[] args) {
final List<Integer> l = List.of(1, 1, 1, 4);
final int f = 2;
final var value = IntStream.range(0, l.size())
.boxed()
.collect(Collectors.groupingBy(i -> i / f, Collectors.groupingBy(l::get, Collectors.counting())));
System.out.println(value);
}
不确定是否这是个人要求,但有时使用标准循环而不是流可能并不是一件坏事。
英文:
Here is a solution.
public static void main(String[] args) {
final List<Integer> l = List.of(1,1,1,4);
final int f = 2;
final var value = IntStream.range(0,l.size())
.boxed()
.collect(Collectors.groupingBy(i -> i/f, Collectors.groupingBy(l::get, Collectors.counting())));
System.out.println(value);
}
Not sure if this is a personal requirement, but sometime using standard loops over streams is not necessarily a bad thing.
答案2
得分: 1
你可以将你的分组收集器包装在CollectingAndThen
收集器中,它接受一个下游收集器和一个完成器函数。在完成器函数中,你可以将值(子列表)修改为一个映射:
List<Integer> list = List.of(1, 1, 1, 4);
int fac = 2;
AtomicInteger ai = new AtomicInteger();
Map<Integer, Map<Integer, Long>> result =
list.stream()
.collect(Collectors.groupingBy(
i -> ai.getAndIncrement() / fac,
Collectors.collectingAndThen(
Collectors.toList(), val -> val.stream()
.collect(Collectors.groupingBy(Function.identity(),
Collectors.counting())))));
System.out.println(result);
英文:
You can wrap your grouping collector in CollectingAndThen
collector which takes a downstream collector and a finisher function. In the finisher you can modify the values (sublists) to a map:
List<Integer> list = List.of(1, 1, 1, 4);
int fac = 2;
AtomicInteger ai = new AtomicInteger();
Map<Integer,Map<Integer,Long>> result =
list.stream()
.collect(Collectors.groupingBy(
i -> ai.getAndIncrement() / fac,
Collectors.collectingAndThen(
Collectors.toList(), val -> val.stream()
.collect(Collectors.groupingBy(Function.identity(),
Collectors.counting())))));
System.out.println(result);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论