Search code examples
javajava-stream

Create a map of maps with counts from list


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.


Solution

  • 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.