0

I have this class:

class Product {
    public double price;

    public Product(double price) {
        this.price = price;
    }
}

And a Map:

Map<Product, Integer> products = new HashMap<>();

That contains several products added like so:

products.put(new Product(2.99), 2);
products.put(new Product(1.99), 4);

And I want to calculate the sum of all products multiple the values using streams? I tried:

double total = products.entrySet().stream().mapToDouble((k, v) -> k.getKey().price * v.getValue()).sum();

But it doesn't compile, I get “Cannot resolve method getValue()”.

I expect:

(2.99 * 2) + (1.99 * 4) = 5.98 + 7.96 = 13.94
Didier L
  • 18,905
  • 10
  • 61
  • 103
Joan P.
  • 2,368
  • 6
  • 30
  • 63

4 Answers4

6

The stream of entries needs single parameter lambda for each entry, not (k,v):

double total = products.entrySet().stream().mapToDouble(e -> e.getKey().price * e.getValue()).sum();
Yassin Hajaj
  • 21,337
  • 9
  • 51
  • 89
DuncG
  • 12,137
  • 2
  • 21
  • 33
3

You can avoid the explicit creation of a doubleStream with something like:

double total = products.entrySet()
                       .stream()
                       .collect(Collectors.summingDouble(e -> e.getKey().price * e.getValue()));
Shawn
  • 47,241
  • 3
  • 26
  • 60
  • Thank you for the alternative solution. Voted-up. – Joan P. Jan 07 '22 at 07:58
  • 1
    But why is “the explicit creation of a `doubleStream`” something to avoid? – Holger Jan 07 '22 at 09:24
  • @Holger Greater efficiency by combining two steps into one isn't enough reason? – Shawn Jan 07 '22 at 11:19
  • 2
    The appearance in source code does not determine what the Stream implementation does. Both approaches perform exactly the same steps, in the same order. But if there’s ever any performance difference for this use case, the dedicated `sum` method is likely to be faster than the generic `collect` operation. – Holger Jan 07 '22 at 11:31
  • @Holger The implementation for `DoubleStream.sum()` uses the same algorithm as `Collectors.summingDouble()`, yup, down to the point of the former creating objects passed to `collect()` to do the work. The latter just calls a mapping function then instead of making a new stream using it first, which means fewer steps. One less pipeline object in the chain to pull data from. Now, I'll grant you it's not likely to be notably more efficient, but from the code it looks like it will definitely save a few function calls for each element. – Shawn Jan 07 '22 at 11:53
1

Not directly related to your question, but I wouldn't use a map for what you are doing. Instead create a new class

public class ProductAmount {
    private Product product;
    private int amount;

    public ProductAmount(Product product, int amount) {
        this.product = product;
        this.amount = amount;
    }

    public double getCombinedPrice() {
        return product.price * amount;
    }
}

Then you can use a List instead of a Map.

List<ProductAmount> products = Arrays.asList(
        new ProductAmount(new Product(2.99), 2),
        new ProductAmount (new Product(1.99), 4)); 
  
products.stream().mapToDouble(ProductAmount::getCombinedPrice).sum();
WJS
  • 36,363
  • 4
  • 24
  • 39
magicmn
  • 1,787
  • 7
  • 15
0

You can also do it like so.

double sum = 0;
for(Entry<Product, Integer> e : products.entrySet()) {
    sum += e.getKey().price * e.getValue();
}
System.out.println(sum);

prints

13.940000000000001

But you have a fundamental flaw in your class. You don't override equals or hashCode. So you're are you using the object reference as the key. Try doing the following:

System.out.println(products.get(new Product(1.99));

It will print null since there is no entry for that reference (it's a different object than the one used to store the value 4).

And finally you should make certain your keys are immutable. Otherwise, circumstances could result in the same error.

Check out why do I need to override hashcode and equals. And since it was mentioned in the comments, also check out what data type to use for money in java.

WJS
  • 36,363
  • 4
  • 24
  • 39