【问题标题】:How to multiply key * value in Map in Java?如何在 Java 中的 Map 中乘以键 * 值?
【发布时间】:2022-01-06 21:18:38
【问题描述】:

我有这门课:

class Product {
    public double price;

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

还有一张地图:

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

其中包含几个这样添加的产品:

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

我想使用流计算所有产品的总和乘以多个值?我试过了:

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

但它无法编译,我得到“无法解析方法getValue()”。

我希望:

(2.99 * 2) + (1.99 * 4) = 5.98 + 7.96 = 13.94

【问题讨论】:

  • 您使用流而不是通过循环遍历地图的.values() 更新的计数变量的任何原因?
  • @Mike'Pomax'Kamermans 我的目标是对每个循环使用流而不是常规。谢谢
  • @JoanP。不要使用像 double 这样的浮点数类型来获得金钱价值。这些可能导致意外的计算结果。见stackoverflow.com/questions/8148684/…stackoverflow.com/questions/3730019/…
  • 在您的 lambda 中,k 已经是键,v 已经是值。这就是它不编译 k.getKey() 的原因(你没有处理 Map.Entry,你已经在处理密钥)

标签: java java-8 java-stream


【解决方案1】:

条目流需要每个条目的单个参数 lambda,而不是 (k,v)

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

【讨论】:

  • 它按预期工作,谢谢。也投了赞成票。
【解决方案2】:

您可以避免显式创建 doubleStream,例如:

double total = products.entrySet()
                       .stream()
                       .collect(Collectors.summingDouble(e -> e.getKey().price * e.getValue()));

【讨论】:

  • 感谢您提供替代解决方案。投票赞成。
  • 但为什么要避免“显式创建doubleStream”?
  • @Holger 将两个步骤合二为一来提高效率还不够吗?
  • 源代码中的外观并不能确定 Stream 实现的作用。两种方法都以相同的顺序执行完全相同的步骤。但是,如果此用例有任何性能差异,专用的 sum 方法可能比通用的 collect 操作更快。
  • @Holger DoubleStream.sum() 的实现使用与Collectors.summingDouble() 相同的算法,是的,直到前者创建传递给collect() 的对象来完成工作。后者只是调用一个映射函数,而不是先使用它创建一个新流,这意味着更少的步骤。链中少一个管道对象可从中提取数据。现在,我承认它不太可能更高效,但从代码看来,它肯定会为每个元素节省一些函数调用。
【解决方案3】:

与您的问题没有直接关系,但我不会为您正在做的事情使用地图。而是创建一个新类

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;
    }
}

然后您可以使用List 而不是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();

【讨论】:

  • 感谢您提供替代解决方案。投票赞成。
【解决方案4】:

你也可以这样做。

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

打印

13.940000000000001

但是你的班级有一个根本性的缺陷。您不会覆盖 equalshashCode。所以你是在使用对象引用作为键。尝试执行以下操作:

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

它将打印null,因为该引用没有条目(它与用于存储值 4 的对象不同)。

最后,您应该确保您的密钥是不可变的。否则,情况可能会导致同样的错误。

查看why do I need to override hashcode and equals。 既然在 cmets 中提到过,也请查看what data type to use for money in java

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-09-06
    • 1970-01-01
    • 2015-10-20
    • 2021-10-26
    • 2021-02-08
    • 1970-01-01
    • 2022-11-03
    • 1970-01-01
    相关资源
    最近更新 更多