-1

I have a HashMap with integer and ArrayList of some Element objects. Element objects are described with price and amount. I want to go through all those elements in each ArrayList, sum them up by calling on each element element.price(), and create a new HashMap which contains old keys from and new values representing summ of each arraylist. Keys for new hash map should remain the same. Trying to do this using streams.

public static HashMap<Integer, Double> findIncomes(HashMap<Integer, ArrayList<Element>> mapa){


    Map<String, Double> m = mapa.entrySet().stream().flatMap()

    return m;
}
Stefan Zobel
  • 3,182
  • 7
  • 28
  • 38
  • 2
    A HashMap may not be sorted. – JB Nizet Aug 20 '19 at 09:31
  • `HashMap`s cannot be indexed so it makes no sense to sort its elements. If you want a sorted map, consider TreeMap. – Phosphorus15 Aug 20 '19 at 09:36
  • A TreeMap is sorted by key, not by value. You shouldn't use a Map, whatever its type is. – JB Nizet Aug 20 '19 at 09:38
  • 1
    **HashMap** is implemented as a hash table, and there is no ordering on keys or values. **TreeMap** is implemented based on red-black tree structure, and it is ordered by the key. **LinkedHashMap** preserves the insertion order. **Hashtable** is synchronized, in contrast to HashMap. – Butiri Dan Aug 20 '19 at 09:39
  • Why not use a `List` instead of a `Map` in the first place? – T A Aug 20 '19 at 09:40
  • Sorting is not so important as summing up those elements and keep those values under same key, from which arraylist they came from..I hope I'm explaining correctly :) –  Aug 20 '19 at 09:41
  • Possible duplicate of [Sort map by value using lambdas and streams](https://stackoverflow.com/questions/29567575/sort-map-by-value-using-lambdas-and-streams) – T A Aug 20 '19 at 09:43
  • @TA Keys in map represent some other objects (in this case `Restaurant` object ) –  Aug 20 '19 at 09:47

2 Answers2

1

The first solution that came to my mind was to use mapToDouble and sum.

That would have looked like this:

public static HashMap<Integer, Double> findIncomes(HashMap<Integer, List<Element>> mapa) {
    HashMap<Integer, Double> sumsByKey = new HashMap<>();
    mapa.entrySet().stream().forEach(entry -> sumsByKey.put(entry.getKey(), entry.getValue().stream().mapToDouble(element -> element.getPrice()).sum()));
    return sumsByKey;
}

But when summing up 1.5d, 5.4d and 6.7d you get 13.600000000000001 as a result.

Therefore I had to remember: When performing calculations with doubles you usually better use BigDecimal.

So a more accurate solution could look like this:

public static HashMap<Integer, Double> findIncomes(HashMap<Integer, ArrayList<Element>> mapa){
    HashMap<Integer, Double> sumsByKey = new HashMap<>();
    mapa.entrySet().stream().forEach(entry -> sumsByKey.put(entry.getKey(),
        entry.getValue().stream().map(element -> BigDecimal.valueOf(element.getPrice())).reduce(BigDecimal.ZERO, BigDecimal::add).doubleValue()));
    return sumsByKey;
}

Since stream in stream is not really readable it might make sense to refactor it further.

MaS
  • 393
  • 3
  • 17
  • Why BigDecimal is necessary for summing? – Kris Aug 20 '19 at 10:09
  • @MaS on the 4th line, why are you using `map()`, is it possible to go with `mapToDouble()` ? –  Aug 20 '19 at 10:18
  • Since I want to map the price of the element to a BigDecimal I can't use mapToDouble. – MaS Aug 20 '19 at 10:20
  • Can you explain more on the precision part you are handling with BigDecimal? I'm quite not getting it :-) – Kris Aug 20 '19 at 10:23
  • @MaS Ok thanks, I if it is not a problem, what is te purpose of reduce operation in this case? Thank you. –  Aug 20 '19 at 10:25
  • @Kris Please see [this](https://stackoverflow.com/questions/13093982/java-double-calculation) for more on this issue. – MaS Aug 20 '19 at 10:32
  • Sure will do, Thanks – Kris Aug 20 '19 at 10:33
  • 1
    @Miljan The reduce-call more or less performs the subsequent add-calls to get a single result (=the sum of). – MaS Aug 20 '19 at 10:34
0

You need a new map always. You cannot alter the same map with different types. Something like this would do the job,

public static HashMap<Integer, Double> findIncomes(HashMap<Integer, ArrayList<Element>> mapa) {

        final HashMap<Integer, Double> m = new HashMap<>();
        mapa.entrySet().stream().forEach(entry -> m.put(entry.getKey(), Double.valueOf(entry.getValue().stream().mapToDouble(Element::price).sum())));
        return m;
    }
Kris
  • 8,680
  • 4
  • 39
  • 67