【问题标题】:Lambdas and Streams (Array)Lambda 和流(数组)
【发布时间】:2018-06-12 16:44:57
【问题描述】:

lambdas 和流的概念有点弱,所以可能有些东西真的没有任何意义,但我会尝试传达我想要发生的事情。

我有一个类 Invoice,其中包含项目名称、价格和数量。 我必须将商品名称映射到总成本(价格*数量)。

虽然它不起作用,但希望它能说明我遇到了什么问题:

invoiceList.stream()
           .map(Invoice::getDesc)
           .forEach(System.out.println(Invoice::getPrice*Invoice::getQty));

我已经知道 forEach 不起作用,因为它映射到变量描述 (getDesc) 而不是 Invoice 对象,我可以使用它的方法获取其他变量。

所以,如果item=pencil, price=1, qty=12,我想要的输出是:

Pencil   12.00

这将在多个 Invoice 对象上完成。

另外,我需要按它们的总数对它们进行排序,并过滤超过一定数量的它们,例如。 100. 将它们放入地图后,我应该怎么做?

【问题讨论】:

    标签: java lambda java-8 java-stream


    【解决方案1】:

    如果您只想打印到控制台,则可以按如下方式完成:

    invoiceList.forEach(i -> System.out.println(i.getName() + "    " + (i.getPrice() * i.getQty())));
    

    如果没有,请继续阅读:

    Using the toMap collector

    Map<String, Double> result = 
         invoiceList.stream()
                    .collect(Collectors.toMap(Invoice::getName, 
                                      e -> e.getPrice() * e.getQuantity()));
    

    这基本上创建了一个映射,其中键是 Invoice 名称,值是给定 Invoice 的发票价格和数量的乘积。

    Using the groupingBy collector

    但是,如果可以有多个同名发票,那么您可以使用groupingBy 收集器和summingDouble 作为下游收集器:

    Map<String, Double> result = 
         invoiceList.stream()
                    .collect(groupingBy(Invoice::getName, 
                      Collectors.summingDouble(e -> e.getPrice() * e.getQuantity())));
    

    这会将Invoice 按名称分组,然后对每个组求和e.getPrice() * e.getQuantity() 的结果。


    更新:

    如果您想要toMap 版本并且过滤结果然后按值升序排序,可以按如下方式完成:

    Map<String, Double> result = invoiceList.stream()
                .filter(e -> e.getPrice() * e.getQuantity() > 100)
                .sorted(Comparator.comparingDouble(e -> e.getPrice() * e.getQuantity()))
                .collect(Collectors.toMap(Invoice::getName,
                        e -> e.getPrice() * e.getQuantity(), 
                        (left, right) -> left,
                        LinkedHashMap::new));
    

    或使用groupingBy 方法:

     Map<String, Double> result =
                    invoiceList.stream()
                            .collect(groupingBy(Invoice::getName,
                                    Collectors.summingDouble(e -> e.getPrice() * e.getQuantity())))
                            .entrySet()
                            .stream()
                            .filter(e -> e.getValue() > 100)
                            .sorted(Map.Entry.comparingByValue())
                            .collect(Collectors.toMap(Map.Entry::getKey,
                                    Map.Entry::getValue, (left, right) -> left,
                                    LinkedHashMap::new));
    

    【讨论】:

    • 这很好用,但是我如何根据变量 e 进行排序?也许从最低到最高。谢谢!
    • 非常感谢,但请问这两种方法到底有什么区别?只是一种不同的方法?
    • @CarameliaBachmann 不,这不仅仅是一种不同的方法。如我的回答中所述,如果发票名称在您的设计模型中是唯一的,则使用toMap,但如果可以有多个具有相同名称的发票对象,则您需要使用groupingBy
    【解决方案2】:

    首先,这是我认为您正在尝试编写的代码:

    invoiceList.stream()
           .forEach(invoice -> System.out.println(invoice.getPrice() * invoice.getQty()));
    

    现在让我们看看这里发生了什么:

    在列表上调用.stream() 会创建一个对象,该对象具有可用于对列表内容进行操作的方法。 foreach 就是这样一种方法,它在每个元素上调用一个函数。 map 是另一种此类方法,但它返回另一个流,其中内容是原始流的内容,但每个元素都替换为您传递给 map 的函数的返回值。

    如果您查看 foreach 调用的内部,您会看到一个 lambda。这定义了一个匿名函数,它将在 invoiceList 的每个元素上调用。 -&gt; 符号左侧的 invoice 变量绑定到流的每个元素,并执行右侧的表达式。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-20
      • 2021-01-12
      • 1970-01-01
      • 2018-06-02
      • 1970-01-01
      • 2018-10-29
      相关资源
      最近更新 更多