Stream.peek改变状态的可能副作用以及为什么不这样使用它

时间:2021-02-21 19:10:04

A solution that I came up on another * question that is using Stream.peek operation works but still seems like is not right because it mutates state in the Stream.peek method.

我在另一个使用Stream.peek操作的*问题上提出的解决方案仍然有效,但似乎仍然不对,因为它在Stream.peek方法中改变了状态。

While researching (here and here) on Stream.peek usage whether it is ok to mutate state I am still not fully convinced that Stream.peek should not mutate state (including state of collection that is source of the Stream).

在研究(此处和此处)Stream.peek使用是否可以改变状态时,我仍然不完全相信Stream.peek不应该改变状态(包括作为Stream源的集合状态)。

Here is what Javadoc says:

这是Javadoc所说的:

This method exists mainly to support debugging, where you want to see the elements as they flow past a certain point in a pipeline:

此方法主要用于支持调试,您希望在元素流经管道中的某个点时查看这些元素:

And then:

Parameters: action - a non-interfering action to perform on the elements as they are consumed from the stream.

参数:action - 在从流中使用元素时对元素执行的非干扰操作。

For well-behaved non-interfering stream sources, the source can be modified before the terminal operation commences and those modifications will be reflected in the covered elements.All the streams returned from JDK collections, and most other JDK classes, are well-behaved in this manner.

对于性能良好的非干扰流源,可以在终端操作开始之前修改源,并且这些修改将反映在覆盖的元素中。从JDK集合和大多数其他JDK类返回的所有流都表现良好。这种方式。

Seems like non-interfering action does includes changing the state of collection in the stream.

似乎非干扰操作确实包括更改流中的收集状态。

Here is the code that uses Stream.peek.

这是使用Stream.peek的代码。

Map< String, List<Test> > userTests = new HashMap<>();

Map< String, List<Test> > filtered  = userTests.entrySet().stream()
        .peek( e -> e.setValue( modifyListAndReturnIt( e.getValue() ) ) )
        .filter( e -> !e.getValue().isEmpty() ) //check if modified list in peek has been emptied
        .collect( Collectors.toMap(p -> p.getKey(), p -> p.getValue() ) );

    public static List<Test> modifyListAndReturnIt(List<Test> list){
        if (somecondition) list.clear();
        return list;
    }

1) Can the above code have any side effect?

1)上述代码可以有任何副作用吗?

2) Why not use peek in such a way. The Javadoc does not seem to not allow it?

2)为什么不以这种方式使用偷看。 Javadoc似乎不允许它?

1 个解决方案

#1


2  

What you seem to do looks harmless as Brian Goetz states in comment here.

你似乎做的事情看起来像Brian Goetz在评论中所说的那样无害。

Now the problem with peek is that if you do side effects inside it - you would expect these side effects to actually happen. So, suppose you would want to alter some property of some object like this:

现在问题是,如果你在其中做副作用 - 你会期望这些副作用实际发生。所以,假设您想要改变某些对象的某些属性,如下所示:

myUserList.stream()
          .peek(u -> u.upperCaseName())
          .count()

In java-8 your peek would be indeed called, in 9 - it is not - there is no need to call peek here since the size can be computed without it anyway.

在java-8中,你的窥视确实会被调用,在9中 - 它不是 - 没有必要在这里调用peek,因为无论如何都可以计算它的大小。

While being on the same path, imagine that your terminal operation is a short-circuit one, like findFirst or findAny - you are not going to process all elements of the source - you might get just a few of them through the pipeline.

在同一条路径上,假设您的终端操作是短路的,比如findFirst或findAny - 您不会处理源的所有元素 - 您可能只通过管道获得其中的一些元素。

Things might get even stranger if your peek would rely on a encounter order even if your terminal operation would not be a short-circuit one. The intermediate operations for parallel processing do not have an encounter order, while the terminal ones - do. Imagine the surprises you might be facing.

即使您的终端操作不是短路操作,如果您的窥视依赖于遭遇订单,事情可能会变得更加奇怪。并行处理的中间操作没有遭遇顺序,而终端处理则没有。想象一下您可能面临的惊喜。

#1


2  

What you seem to do looks harmless as Brian Goetz states in comment here.

你似乎做的事情看起来像Brian Goetz在评论中所说的那样无害。

Now the problem with peek is that if you do side effects inside it - you would expect these side effects to actually happen. So, suppose you would want to alter some property of some object like this:

现在问题是,如果你在其中做副作用 - 你会期望这些副作用实际发生。所以,假设您想要改变某些对象的某些属性,如下所示:

myUserList.stream()
          .peek(u -> u.upperCaseName())
          .count()

In java-8 your peek would be indeed called, in 9 - it is not - there is no need to call peek here since the size can be computed without it anyway.

在java-8中,你的窥视确实会被调用,在9中 - 它不是 - 没有必要在这里调用peek,因为无论如何都可以计算它的大小。

While being on the same path, imagine that your terminal operation is a short-circuit one, like findFirst or findAny - you are not going to process all elements of the source - you might get just a few of them through the pipeline.

在同一条路径上,假设您的终端操作是短路的,比如findFirst或findAny - 您不会处理源的所有元素 - 您可能只通过管道获得其中的一些元素。

Things might get even stranger if your peek would rely on a encounter order even if your terminal operation would not be a short-circuit one. The intermediate operations for parallel processing do not have an encounter order, while the terminal ones - do. Imagine the surprises you might be facing.

即使您的终端操作不是短路操作,如果您的窥视依赖于遭遇订单,事情可能会变得更加奇怪。并行处理的中间操作没有遭遇顺序,而终端处理则没有。想象一下您可能面临的惊喜。