流上的 reduce() 操作似乎正在修改数据源(列表)Stream API Java 8

Posted

技术标签:

【中文标题】流上的 reduce() 操作似乎正在修改数据源(列表)Stream API Java 8【英文标题】:reduce() operation on stream seems to be modifying the source of data (list) Stream API Java 8 【发布时间】:2019-07-02 11:15:45 【问题描述】:

我有一个名为 Transaction 的简单 POJO,它具有三个私有属性 String type、double amount 和 String id。 在主类中,我创建了几个调用构造函数的事务实例,如下所示 -

List<Transaction> transList = Arrays.asList(new Transaction(Transaction.TRANSACTION_TYPE_GROCERY,45.50,"2a"),
                                            new Transaction(Transaction.TRANSACTION_TYPE_GROCERY,50.0,"1a"),
                                            new Transaction(Transaction.TRANSACTION_TYPE_GROCERY,15.00,"3a"),
                                            new Transaction(Transaction.TRANSACTION_TYPE_GROCERY,27.43,"4a"),
                                            new Transaction(Transaction.TRANSACTION_TYPE_CLOTHING,145.0,"5a"),
                                            new Transaction(Transaction.TRANSACTION_TYPE_CLOTHING,105.0,"6a"));

现在我使用下面的代码在这个列表上调用了下面的操作 -

Optional<Transaction> totalA = transList.stream()
.filter(x->x.getType()==Transaction.TRANSACTION_TYPE_GROCERY)
.reduce((a,b) -> Transaction z = b;                                                                             
                  z.setAmount(a.getAmount()+b.getAmount());
                  return z;);

在这里,我尝试通过将 Transaction 保持为最低单位来执行归约操作,并计算所有交易金额的总和并将其设置在新的 Transaction z 中。所有这些最终都存储为可选。 在此之后,如果我尝试对 transList 数据源执行任何其他操作,我会得到不正确的结果,因为 transList 的状态会被破坏。

List<String> transactionIds = transList.stream()
                                                .filter(x -> x.getAmount()>50.00)
                                                .map(Transaction::getId)
                                                .collect(Collectors.toList());
System.out.println(transactionIds);

我已经使用 reduce() 成功地为这个列表完成了 Optional 包含 Double 项和 double 返回值的实现。 我只想知道 Optional 出了什么问题,以至于它最终修改了数据源本身,这不应该发生,因为 Stream 是正常的。

【问题讨论】:

System.out.println(transactionIds) 的结果;应该是 [5a, 6a]。但是,上面的代码在执行时会返回 [1a, 3a, 4a, 5a, 6a]。 不要评论您的问题。 编辑它。 这看起来很可疑 - Transaction z = b; z.setAmount(a.getAmount()+b.getAmount()); 将 b 复制到 z 不会创建对象的副本,因此您实际上是在修改 b。 一方面,您的第一个流管道中的reduce 包含一个缺陷,而您返回一个新的Transaction 并添加了amount,并且因为type 无论如何对他们来说都是相同的(原因:过滤器),您在操作中没有考虑id 属性。这就是导致您也不会坚持所有不同的ids 的原因。另一方面,修改现有对象实际上会立即提出这一点。 【参考方案1】:

lambda:

(a,b) -> Transaction z = b;                                                                             
                  z.setAmount(a.getAmount()+b.getAmount());
                  return z;

正在修改b 参数。请记住,分配确实复制对象,因此Transaction z = b 只是为b 指向的对象提供别名。

您可能应该使用允许指定标识和组合器的reduce 重载,操作系统只需创建对象的副本。

【讨论】:

谢谢。你们三个都回答了我的问题。【参考方案2】:

使用 Optional 一切正常。您的对象是引用数据类型。 在这里,您复制了对象 Transaction z = b; 的引用。它不会创建一个新的。现在两个变量都指向同一个Transaction。而且无论您修改zb,它最终都会修改源数据

这可能有用https://javarevisited.blogspot.com/2015/09/difference-between-primitive-and-reference-variable-java.html

【讨论】:

谢谢。到目前为止,所有三个答案都正确解释了我的查询。【参考方案3】:
Transaction z = b;// doesn't create a new object.  
                                                    z.setAmount(a.getAmount()+b.getAmount()); //you actually set a amout to `b` object

return z; // and here you returm `b` object 

【讨论】:

谢谢。到目前为止,所有三个答案都是正确的解释。

以上是关于流上的 reduce() 操作似乎正在修改数据源(列表)Stream API Java 8的主要内容,如果未能解决你的问题,请参考以下文章

Google 隐式 Oauth 流上的操作失败...出了啥问题?

用PowerDesigner画数据流图,如何在数据流上加文本?

IO流上:概述字符流缓冲区

如何在 Java 流上调用多个终端操作

Java:如何在正在保存的流上即时计算sha1摘要?

如何在 webrtc 的画布流上添加音频流