opWrapSink()方法是如何被调用的?

huangapple go评论79阅读模式
英文:

How is opWrapSink() method called?

问题

Java中map()方法的实现如下

    `public final <R> Stream<R> map(Function<? super P_OUT, ? extends R> mapper) {
        Objects.requireNonNull(mapper);
        return new StatelessOp<P_OUT, R>(this, StreamShape.REFERENCE,
                                     StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) {
            @Override
            Sink<P_OUT> opWrapSink(int flags, Sink<R> sink) {
                return new Sink.ChainedReference<P_OUT, R>(sink) {
                    @Override
                    public void accept(P_OUT u) {
                        downstream.accept(mapper.apply(u));
                    }
                };
            }
        };
    }`
    
该方法返回一个新的StatelessOp对象它基本上是一个新的流扩展自ReferencePipeline并实现了Stream接口)。我们有一个重写的opWrapSink()方法它返回一个新的Sink.ChainedReference对象在accept方法内部我们最终使用mapper对象该对象负责调用我们在参数中传递的逻辑

我不明白的是opWrapSink()方法在什么时候以及如何被调用因为map()只是返回一个新对象仅此而已JVM是否在后台调用该方法我在这里漏掉了什么
英文:

The implemenation of map() method in java looks like this:

`public final &lt;R&gt; Stream&lt;R&gt; map(Function&lt;? super P_OUT, ? extends R&gt; mapper) {
    Objects.requireNonNull(mapper);
    return new StatelessOp&lt;P_OUT, R&gt;(this, StreamShape.REFERENCE,
                                 StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) {
        @Override
        Sink&lt;P_OUT&gt; opWrapSink(int flags, Sink&lt;R&gt; sink) {
            return new Sink.ChainedReference&lt;P_OUT, R&gt;(sink) {
                @Override
                public void accept(P_OUT u) {
                    downstream.accept(mapper.apply(u));
                }
            };
        }
    };
}`

The method returns a new StatelessOp object which is basically a new stream (extends ReferencePipeline and implements Stream). We have an overrriden opWrapSink() method that returns a new Sink.ChainedReference object. And insdide accept method we finally use mapper object that is responsible for invoking logic we pass in the parameter.

What I dont get is at what point and how opWrapSink() method is called. Cause map() just returns a new object, thats it. Does JVM call the method in the background. What am I missing here?

答案1

得分: 2

一个流对象是一个流水线概念。它描述了一系列要执行的操作。

想象一下将水引到你的水龙头的管道。

我可以在管道中安装一个过滤器。我也可以在其中一个管段中安装加热元件。

在我打开水龙头之前,所有这些都不会产生任何影响 - 在此之前,我只是安装了一些在水流开始流动时会执行操作的东西,但它们本身不会导致水流动。

流的工作方式也是如此。例如,尝试以下操作:

var example = new ArrayList<String>();
example.add("Hello");
example.add("World!");
example.stream().map(x -> { System.out.println(x); return x; });

然后运行它。奇怪的是,这将什么也不会打印出来 - 显然,那个 map 操作似乎什么也没做!但那是因为它就像水龙头管道中的过滤器:在你打开水龙头之前,那个过滤器不会起任何作用,而且还没有人打开水龙头。您实际上需要请求流提供数据,一旦您这样做,该流将提供数据,并且可能会通过从源流(在这里就是 example ArrayList)读取,经过 map 操作来提供数据。因此,这段代码:

example.stream().map(x -> { System.out.println(x); return x; }).max(Comparator.naturalOrder());

实际上会打印出 HelloWorld,因为流的 max() 方法会打开水龙头。这些通常称为“终端”操作,你可以通过它们来识别,一般情况下,它们 会返回另一个流,而会返回其他东西。collectmaxforEach(通常不是你想要的),findFirst等 - 这些都是终端操作。

流封装了数据源以及你想要在该数据上执行的任何中间操作(过滤、映射、扁平映射等等)。它通常不会封装数据本身(即流保存对列表的引用,而不是对其中所有数据的引用 - 请注意,流是抽象的,它们如何提供数据取决于实现。我只是在告诉你通常的情况)。它代表了你房子里的管道系统。它既不代表水公司,也不代表水龙头,更不代表打开水龙头的人。

注:如果您实际上想要执行一项类似于在流动过程中打印每个元素的辅助检查工作,您可以使用 peek - 我这里只是滥用了 .map() 调用来展示它的工作原理。

英文:

A stream object is a pipeline concept. It's a description of a series of operations to perform.

Imagine the piping that brings water to your faucet.

I could install a filter in your pipes. I could also install a heater element in one of the pipe segments.

None of that is going to have any effect whatsoever until I turn on the faucet - until then I've just installed things that will perform operations once water starts flowing, but they do not themselves cause water to flow.

Streams are the same way. For example, try this:

var example = new ArrayList&lt;String&gt;();
example.add(&quot;Hello&quot;);
example.add(&quot;World!&quot;);
example.stream().map(x -&gt; { System.out.println(x); return x; });

and run it. "Weirdly", this prints nothing - clearly that map operation seems to do nothing! But that's because it's like that filter in your faucet pipe: That filter isn't going to do anything until you turn on the faucet, and nobody turned on that faucet yet. You need to actually ask a stream to provide data, and once you do so, that stream will provide them, and presumably, it will do so via reading from the origin stream (here, what the example ArrayList made), through the map operation. Thus, this:

example.stream().map(x -&gt; { System.out.println(x); return x; }).max(Comparator.naturalOrder());

WOULD in fact print Hello and World, because the max() method of streams turns on the faucet. These are generally called the 'terminal' operations, and you recognize them, generally, by the fact that they do not themselves return a stream, but return something else. collect, .max, forEach (that's usually not what you want), findFirst, etc - those are terminal operations.

A stream encapsulates both the source of the data as well as any intermediate operations you want done on that data (filtering, mapping, flatmapping, etcetera). It does not as a rule encapsulate the data itself (i.e. a stream holds a reference to a list, it doesn't hold a reference to all the data inside it, for example - note that streams are abstract, how they actually give you that data is up to the implementation. I'm just telling you about the usual way it goes). It represents the piping in your house. It does not represent either the water company, or the faucet, or the person opening it.

NB: If you actually want to do a side-show inspection job such as print every element as it is streamed along, you'd use peek - I'm abusing the .map() call here merely to show you how it works.

huangapple
  • 本文由 发表于 2020年10月27日 20:13:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/64554226.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定