Steam操作修改实际列表

时间:2019-05-02 18:04:04

标签: java java-stream

我已经读过某个地方,流操作总是在终端操作处返回一个新的集合,而不更改已应用流操作的原始集合。

但是在我看来,原始列表已被修改。

 return subscriptions.stream()
            .filter(alertPrefSubscriptionsBO -> (alertPrefSubscriptionsBO.getType() == AlertPrefContactTypeEnum.PRIMARY_CONTACT || alertPrefSubscriptionsBO.getType() == AlertPrefContactTypeEnum.SECONDARY_CONTACT))
            .map(alertPrefSubscriptionsBO -> {
                if (alertPrefSubscriptionsBO.getType() == AlertPrefContactTypeEnum.PRIMARY_CONTACT) {
                    alertPrefSubscriptionsBO.setType(AlertPrefContactTypeEnum.PRIMARY);
                } else
                    alertPrefSubscriptionsBO.setType(AlertPrefContactTypeEnum.SECONDARY);

                return alertPrefSubscriptionsBO;
            })
            .collect(groupingBy(AlertPrefSubscriptionsBO::isActiveStatus, groupingBy(AlertPrefSubscriptionsBO::getAlertLabel, Collectors.mapping((AlertPrefSubscriptionsBO o) -> o.getType()
                    .getContactId(), toSet())
            )));

在此操作之后,已修改订阅列表,仅包含AlertPrefContactTypeEnum.PRIMARY和AlertPrefContactTypeEnum.SECONDARY对象。我的意思是列表的大小保持不变,但值已更改。

1 个答案:

答案 0 :(得分:3)

那是因为您违反map(Function<? super T,? extends R> mapper)方法的合同:

  

参数:
  映射器-一个non-interferingstateless函数,可应用于每个元素

您违反了“无状态”部分:

  

无状态行为

     如果流操作的行为参数是有状态的,则流管线结果可能不确定或不正确。有状态的lambda(或其他实现适当功能接口的对象)是一种有状态的lambda,其结果取决于在流管道执行期间可能更改的任何状态。有状态lambda的一个示例是map()中的参数:

Set<Integer> seen = Collections.synchronizedSet(new HashSet<>());
stream.parallel().map(e -> { if (seen.add(e)) return 0; else return e; })...
     

在这里,如果并行执行映射操作,则由于线程调度差异,同一输入的结果可能因运行而不同,而使用无状态lambda表达式时,结果将始终相同。

     

还请注意,尝试从行为参数访问可变状态会给您带来关于安全性和性能的错误选择;如果您不同步对该状态的访问,则将导致数据争用,因此代码将被破坏,但是,如果您确实同步对该状态的访问,则可能会导致争用破坏寻求从中受益的并行性。最好的方法是避免使用有状态的行为参数来完全流式处理操作。通常,有一种方法可以重组流管道以避免状态化。

实现该映射操作的正确方法是复制alertPrefSubscriptionsBO并为 copy 提供新的类型。

遵循java.time类使用的样式,例如参见ZonedDateTime的所有withXxx(...)方法,您可以将alertPrefSubscriptionsBO对象设置为或不可变,并更改获取具有属性的副本的方法,例如在类上使用方法withType(...)并使用AlertPrefContactTypeEnum枚举的静态导入,您的代码可能是:

.map(bo -> bo.withType(bo.getType() == PRIMARY_CONTACT ? PRIMARY : SECONDARY))