1

I have class Data which have 4 fields and have list of values then i want replace each row groupby year and sum value for same year using java 8 and stream. please see below code and output.

class Data {
    int id;
    int year;
    String name;
    double value;
}

List<Data> list = new ArrayList<>();
list.add(new Data(101, 2018, "AAA", 20));
list.add(new Data(102, 2019, "BBB", 30));
list.add(new Data(103, 2020, "CCC", 10));
list.add(new Data(104, 2019, "DDD", 50));
list.add(new Data(105, 2020, "EEE", 40));
list.add(new Data(106, 2020, "FFF", 60));

First code try:

list.stream().collect(Collectors.groupingBy(Data::getYear, Collectors
    .summingDouble(Data::getValue)));

Second code try:

list.stream().collect(Collectors.toMap(value -> value.getYear(), Function.identity(),
            (a, b) -> new Data(a.getId(), a.getYear(), a.getName(), a.getValue() + b.getValue()))).values()
            .forEach(value -> System.out.println(value));

Output:

id    year   name   value
101   2018   "AAA"   20
102   2019   "BBB"   80
103   2020   "CCC"   110

Expected Output:

id    year   name   value
101   2018   "AAA"   20
102   2019   "BBB"   80
103   2020   "CCC"   110
104   2019   "DDD"   80
105   2020   "EEE"   110
106   2020   "FFF"   110 
  • A stream cannot both group by some field and yet output all ungrouped values at the same time. This must thus be a two-step process: first aggregate by year in a map like you did (using the first code is fine), then use that map to re-attach the aggregated values to the original list – i.e. process the list a second time and enrich it with the values from the map. As this looks like a school exercise, I think it’s better if you try this yourself and then edit your question with your attempt. – Didier L Feb 02 '22 at 23:15
  • Side note, this looks like the same exercise as from [this question](https://stackoverflow.com/q/70926438/525036) from 2 days ago – although the formatting is much better, to be honest – and at least the code compiles :) – Didier L Feb 02 '22 at 23:29
  • @DidierL Thanks for your reply, It may sound like a school exercise, but sometimes even after a lot of effort, the result is not getting, then it is better to ask here. FYI, I have just started using stacloverflow. – Rishi Kumar Feb 03 '22 at 00:18
  • @AlexanderIvanchenko I am trying to do this but getting popup like: Thanks for the feedback! You need at least 15 reputation to cast a vote, but your feedback has been recorded. – Rishi Kumar Feb 03 '22 at 00:31
  • @AlexanderIvanchenko [this question](https://stackoverflow.com/questions/70926438/java-8-how-to-solve-this-problem-using-in-java-8) If it happens that ID end Year which is in second class and I want same output after inner join of both list in base of id then how can do it? – Rishi Kumar Feb 03 '22 at 00:39
  • @RishiKumar `how can do it?` - Well, first of all, there is a crucial notion with regards to stream and functions that is called [Pure function](https://stackoverflow.com/questions/11153796/benefits-of-pure-function). Shortly function that is pure doesn't cause side-effects and doesn't mutate objects that are passed to it. If you'll have a ferme understanding of this notion it'll give you a lot of points while talking about streams with your professor or during the job interview. Don't look at those people that are trying to implement everything using streams. – Alexander Ivanchenko Feb 03 '22 at 02:09

1 Answers1

1

Map created by the second stream in your code preserves only a single entry per year.

Remaping function (a, b) -> new Data(a.getId(), a.getYear(), a.getName(), a.getValue() + b.getValue())) takes care about the duplicates so that value of each data object will be the sum of all values.

In order to retain all the Data and change the value of every object to be a total value for the given year, you have to take several steps:

  • create a map: year --> total value per year (which is already done correcl);
  • create a map: year --> list of Data objects;
  • apply the total value per year to each of the Data objects.
    public static void main(String[] args) {
        List<Data> dataList = List.of(
                new Data(101, 2018, "AAA", 20),
                new Data(102, 2019, "BBB", 30),
                new Data(103, 2020, "CCC", 10),
                new Data(104, 2019, "DDD", 50),
                new Data(105, 2020, "EEE", 40),
                new Data(106, 2020, "FFF", 60)
        );

        Map<Integer, Double> yearToTotalVal = getTotalValueMap(dataList);

        Map<Integer, List<Data>> yearToData = getYearToDataListMap(dataList);

        appllyTotalValue(yearToTotalVal, yearToData);

        for (Map.Entry<Integer, List<Data>> entry: yearToData.entrySet()) {
            System.out.println(entry);
        }
    }
    private static Map<Integer, Double> getTotalValueMap(List<Data> dataList) {
        return dataList.stream()
                .collect(Collectors.groupingBy(Data::getYear,
                                    Collectors.summingDouble(Data::getValue)));
    }

    private static Map<Integer, List<Data>> getYearToDataListMap(List<Data> dataList) {
        return dataList.stream()
                .collect(Collectors.groupingBy(Data::getYear));
    }
    private static void appllyTotalValue(Map<Integer, Double> yearToTotalVal, 
                                         Map<Integer, List<Data>> yearToData) {
        for (Integer year: yearToTotalVal.keySet()) {
            yearToData.get(year)
                    .replaceAll(data -> new Data(data.getId(),
                                                data.getYear(),
                                                data.getName(),
                                                yearToTotalVal.get(year)));
        }
    }

output - data for each year:

2018=[Data{id=101, year=2018, name='AAA', value=20.0}]
2019=[Data{id=102, year=2019, name='BBB', value=80.0}, Data{id=104, year=2019, name='DDD', value=80.0}]
2020=[Data{id=103, year=2020, name='CCC', value=110.0}, Data{id=105, year=2020, name='EEE', value=110.0}, Data{id=106, year=2020, name='FFF', value=110.0}]

Side note:

  • I also advise you to make the Data class immutable
    public class Data {
        private final int id;
        private final int year;
        private final String name;
        private final double value;

        // Constructor, getters, etc
    }
Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46