Using the map as the output type, one could have a potentially endless list of reducers, each of which created its own statistics and added it to the map.
public static <K, V> Map<K, V> addMap(Map<K, V> map, K k, V v) { Map<K, V> mapout = new HashMap<K, V>(); mapout.putAll(map); mapout.put(k, v); return mapout; }
...
List<Person> persons = new ArrayList<>(); persons.add(new Person("Person One", 1, 18)); persons.add(new Person("Person Two", 1, 20)); persons.add(new Person("Person Three", 1, 30)); persons.add(new Person("Person Four", 2, 30)); persons.add(new Person("Person Five", 2, 29)); persons.add(new Person("Person Six", 3, 18)); List<BiFunction<Map<String, Integer>, Person, Map<String, Integer>>> listOfReducers = new ArrayList<>(); listOfReducers.add((m, p) -> addMap(m, "Count", Optional.ofNullable(m.get("Count")).orElse(0) + 1)); listOfReducers.add((m, p) -> addMap(m, "Sum", Optional.ofNullable(m.get("Sum")).orElse(0) + p.i1)); BiFunction<Map<String, Integer>, Person, Map<String, Integer>> applyList = (mapin, p) -> { Map<String, Integer> mapout = mapin; for (BiFunction<Map<String, Integer>, Person, Map<String, Integer>> f : listOfReducers) { mapout = f.apply(mapout, p); } return mapout; }; BinaryOperator<Map<String, Integer>> combineMaps = (map1, map2) -> { Map<String, Integer> mapout = new HashMap<>(); mapout.putAll(map1); mapout.putAll(map2); return mapout; }; Map<String, Integer> map = persons .stream() .reduce(new HashMap<String, Integer>(), applyList, combineMaps); System.out.println("map = " + map);
Produces:
map = {Sum=10, Count=6}