-2

I am trying to convert the following Java code to Java 8. I have written the following code to calculate the average temperatures.

public static double calculateTemps(String country, Map<String, List<Temperatures>> tempMap) {
    double temp = 0.0f;
    int count = 0;

    if (country.equalsIgnoreCase("India")) {
        for (Map.Entry<String, List<Temperatures>> m : tempMap.entrySet()) {
            for (Temperatures t : m.getValue()) {
                temp = temp + t.getTemp();
                count++;
            }
        }
    }
    System.out.println(count);
    return temp / count;
}

The above code is working fine. Now, I am trying to convert it to Java 8. I have written the following code, but I am getting compile-time errors

public static double calculateTemps(String country, Map<String, List<Temperatures>> tempMap) {
    double temp = 0.0f;
    int count = 0;

    tempMap.entrySet().stream().filter(temps -> temps.getKey()
                    .equalsIgnoreCase("India"))
            .forEach(temps -> {
                temp = temp + temps.getValue();
            }).collect(Collectors.toSet());
}

I thought map is better suited here, but after going through a few questions on Stack Overflow I thought for-each is better suited here. Not sure. Could anyone please enlighten me?

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
nikhil udgirkar
  • 367
  • 1
  • 7
  • 23
  • 2
    You should use reduce instead of forEach, to sum your filtered entries. And there's no reason to collect since you don't need that collection for something. – Just another Java programmer Jul 15 '22 at 05:45
  • 3
    Note that the code is already Java 8. What you actually means is ... how should you convert it to use Java 8+ streams. Also, note that converting "looping" code to use streams won't necessarily improve it. – Stephen C Jul 15 '22 at 06:01

3 Answers3

3

Your code has multiple problems:

  1. You're trying to add Temperatures objects to temp instead of Temperatures.getTemp()
  2. You're trying to modify variable temp inside a lambda, but temp must be effectively final
  3. You're calling collect on the void method forEach, which is not possible.

You can make use of features of DoubleStream to calculate the average:

return tempMap.entrySet().stream()
        .filter(temps -> temps.getKey().equalsIgnoreCase("India"))
        .flatMap(temps -> temps.getValue().stream())
        .mapToDouble(temp -> temp.getTemp())
        .average().orElse(0.0);

As an aside, the filter condition is different from the condition used in the original method (which checked against the country parameter), but I have preserved it from your original attempt. Check carefully if it is really what you need.

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
  • Thanks for the code Point#1. Corrected and understood #2. Do you have any online source pointing to variable declarations inside lambda expressions #3. My bad. for-Each and collect are terminal ops. By mistake I had added both at last minute – nikhil udgirkar Jul 15 '22 at 06:12
  • 1
    @nikhiludgirkar For example, [Variable used in lambda expression should be final or effectively final](https://stackoverflow.com/questions/34865383/variable-used-in-lambda-expression-should-be-final-or-effectively-final) – Mark Rotteveel Jul 15 '22 at 06:14
  • Actually, the original Code uses `country.equalsIgnoreCase("India")`, where you use `temps.getKey().equalsIgnoreCase("India"))`. – Dorian Gray Jul 16 '22 at 11:40
  • @DorianGray I explicitly point that out below the code. I did that as it was what the OP did in their original attempt to translate this to streams. – Mark Rotteveel Jul 16 '22 at 15:35
2

You can try this

public static double calculateTemps(String country, Map<String, List<Temperatures>> tempMap) {
    return tempMap.entrySet()
                  .stream()
                  .filter(temps -> temps.getKey().equalsIgnoreCase("India"))
                  .flatMap(entry -> entry.getValue().stream())
                  .mapToDouble(Temperatures::getTemp)
                  .average()
                  .orElse(0.0);
}
Trash Can
  • 6,608
  • 5
  • 24
  • 38
1

you can reference like this:

    public static double calculateTemps(String country, Map<String, List<Temperatures>> tempMap) {
        double temp = 0.0f;
        int count = 0;

        if (country.equalsIgnoreCase("India")) {

            double result = tempMap.entrySet().stream()
                    .flatMap(entry -> entry.getValue().stream())
                    .mapToDouble(Temperatures::getTemp)
                    .average().orElse(0.0);
            System.out.println(result);
        }

        return 0;
    }
泥瓦匠
  • 96
  • 6