Home > Net >  Java stream map of map of list
Java stream map of map of list

Time:09-21

I have an entity that looks like this:

public class Snippet {
    private Integer docId;
    private Integer page;
    private Payload payload;
}

The input data is a List<Snippet>

I need to create an index that allows us to iterate over docIds and pages and obtain the associated Snippet objects.

So a data structure like this:

Map<Integer, Map<Integer, List<Snippet>>>

I can use Java streams to get a Map<Integer, Map<Integer, Snippet>> - but this won't collect the list of Snippets at the end.

        List<Snippet> input = ....;
        input.stream()
        .collect(Collectors.groupingBy(
                    Snippet::getDocId, 
                    Collectors.toMap(Snippet::getPage, Function.identity())
                 )
        );

How can I do the collecting to get a List as the final map value?

CodePudding user response:

As you can see, Collectors.groupingBy(..) gets collector as 2nd argument. So, just use Collectors.groupingBy(..) as 2nd argument of Collectors.groupingBy(..)

input.stream().collect(Collectors.groupingBy(
                        Snippet::getDocId,
                        Collectors.groupingBy(Snippet::getPage,
                                Collectors.toList())));

CodePudding user response:

Here is another alternative for your current or future consideration. It uses the computeIfAbsent method of the Map Interface.

  • create the final content map.
  • iterate thru the list of Snippets
  • if the DocId is not present, create the entry with DocId as key. Then try and store the page and Snippet
  • if the page is not present, create the entry with page as key. Then store the Snippet in the list.

Here is an example using records in place of classes.

record PayLoad(String getValue) {
    @Override
    public String toString() {
        return "["   getValue()   "]";
    }
}

record Snippet(int getDocId, int getPage,
        PayLoad getPayLoad) {
    @Override
    public String toString() {
        return String.format("{%s, %s, %s}", getDocId,
                getPage, getPayLoad);
    }
}

Create some data

List<Snippet> input =
        List.of(new Snippet(1, 1, new PayLoad("A")),
                new Snippet(1, 2, new PayLoad("B")),
                new Snippet(1, 3, new PayLoad("C")),
                new Snippet(2, 1, new PayLoad("D")),
                new Snippet(2, 2, new PayLoad("E")),
                new Snippet(2, 3, new PayLoad("F")));

Store the snippets

Map<Integer, Map<Integer, List<Snippet>>> map =
        new HashMap<>();

for (Snippet s : input) {
    map.computeIfAbsent(s.getDocId(), v -> new HashMap<>())
            .computeIfAbsent(s.getPage(),
                    v -> new ArrayList<>())
            .add(s);
}

Print them

map.forEach((k, v) -> {
    System.out.println(k);
    v.forEach((kk, vv) -> System.out
            .println("          "   kk   " -> "   vv));
});

prints

1
          1 -> [{1, 1, [A]}]
          2 -> [{1, 2, [B]}]
          3 -> [{1, 3, [C]}]
2
          1 -> [{2, 1, [D]}]
          2 -> [{2, 2, [E]}]
          3 -> [{2, 3, [F]}]
  • Related