Search code examples
javalistjava-streamcollectors

Java Stream Generate Map from List of Objects


I have a class like this.

public class Foo {
    private String prefix;
    private String sector;
    private int count;
}

Given a foo list:

//Args: prefix, sector, count
fooList.add(new Foo("44",,"RowC", 1 ));
fooList.add(new Foo("1",,"Rowa", 1 ));
fooList.add(new Foo("1",,"RowB", 1 ));
fooList.add(new Foo("1",,"Rowa", 1 ));

And I need to return the request an object sorted by Prefix asc like this:

{
  "1": {
    "Rowa": "2",
    "RowB": "1"
  },
  "44": {
    "RowC": "1"
  }
}

So the problem is: I have to group the list by the prefix, and then show, every sector and the count(*) of items on the list with the same row and sector. The far that I got is using stream like this:

fooList.stream()
       .collect(Collectors.groupingBy(
                Foo::getPrefix,
                Collectors.groupingBy(
                        Foo::getSector,
                        Collectors.mapping(Foo::getSector , Collectors.counting())
                )
        ));

The problem is, that the code above, is that the count is a Long, and I need to return as a String. I've tried with .toString but it gives me an error (Can assign java.lang.String to java.util.stream.Collector).

UPDATE

With the help of Andreas and Naman, now I can map count as String. I just need it sorted by Prefix.

Can anyone help me?


Solution

  • You were almost there, just replace the Collectors.mapping line with:

    Collectors.summingInt(Foo::getCount))
    

    As in:

    List<Foo> fooList = new ArrayList<>();
    fooList.add(new Foo("44", "RowC", 1 ));
    fooList.add(new Foo("1", "Rowa", 1 ));
    fooList.add(new Foo("1", "RowB", 1 ));
    fooList.add(new Foo("1", "Rowa", 1 ));
    
    Map<String, Map<String, String>> result = fooList.stream().collect(
            Collectors.groupingBy(
                    Foo::getPrefix,
                    TreeMap::new, // remove if prefix sorting not needed
                    Collectors.groupingBy(
                            Foo::getSector,
                            () -> new TreeMap<>(Collator.getInstance()), // remove if sector sorting not needed
                            Collectors.collectingAndThen(
                                    Collectors.summingInt(Foo::getCount),
                                    String::valueOf
                            )
                    )
            )
    );
    
    System.out.println(result); // prints: {1={Rowa=2, RowB=1}, 44={RowC=1}}
    

    Notice the TreeMap constructors added to the groupingBy() calls, which ensures that the maps are sorted. The first is sorting lexicographically, while the second sorts according to spoken language, i.e. upper- vs lowercase doesn't affect order.