Take you to understand Stream from the principle analysis

summary: Stream is a new feature provided to us by jdk1.8

This article is shared from Huawei Cloud Community " Analysis of the Principles of Deep Understanding of Stream ", author: Li Ge Technology.

Stream is a new feature provided to us by jdk1.8. It mainly allows us to process data collections in a declarative way . We need to know that our collections are our most commonly used data storage structures in projects . To do some filtering or other operations on elements, we generally use for loops.

Stream operation classification

Operations in Stream can be divided into two categories: intermediate operations and end operations.

The intermediate operation will only record the operation, and only the end operation will trigger the actual calculation, which can be understood as lazy loading, which is one of the reasons why Stream is so efficient when iteratively calculating large objects.

Intermediate operations are divided into stateful operations and stateless operations. Stateless means that the processing of elements is not affected by previous elements, and stateful means that the operation can only continue after all elements are obtained. This is also easier to understand, such as the stateful distinct() deduplication method, do you think he can not care about other values? Of course not, he must get all the elements to know whether the current iteration element is repeated.

The end operation can be divided into short-circuit and non-short-circuit operations. This should be well understood. Short-circuit means that the final result can be obtained by encountering certain elements that meet the conditions; non-short-circuit means that all elements must be processed to obtain the final result.

The reason for such fine-grained division is that the underlying layers handle each situation differently.

Stream structure analysis

Let's take a brief look at the following piece of code:

 List<String> list = new ArrayList<>();
 // 获取stream1
 Stream<String> stream1 = list.stream();
 // stream1通过filter后得到stream2
 Stream<String> stream2 = stream1.filter("lige"::equals);
 // stream1与stream2是同一个对象吗?
 System.out.println("stream1.equals(stream2) = " + stream1.equals(stream2));
 System.out.println("stream1.classTypeName = " + stream1.getClass().getTypeName());
 System.out.println("stream2.classTypeName = " + stream2.getClass().getTypeName());
 // 结果
 // stream1.equals(stream2) = false
 // stream1.classTypeName = java.util.stream.ReferencePipeline$Head
 // stream1.classTypeName = java.util.stream.ReferencePipeline$2

Obviously, stream1 and stream2 are not the same object, and they are not the same implementation class. The implementation class of stream1 is ReferencePipeline$Head, and the implementation class of stream2 is an anonymous inner class. Let us analyze its source code. There is nothing to hide under the so-called source code.

Let's look at stream2 again:

Through analysis, we can find that the implementation class of stream2 is StatelessOp, so such a structure is formed.

Each intermediate operation will generate a new Stream. If it is a stateless operation, the implementation class is StatelessOp, and if it is a stateful operation, the implementation class is StatefulOp.

Let's look at the inheritance relationship between them again.

Let's talk about the core sink again

In fact, the essence of the internal implementation of the Stream API is how to overload the four interface methods of Sink.

I still start with an example:

List<String> list = new ArrayList<>();
list.add("zhangsan");
list.add("ligeligeligeligeligeligeligeligeligelige");
list.add("lisilisilisilisilisilisilisilisi");
list.add("wangwu");
list.add("ligejishuligejishuligejishuligejishuligejishuligejishuligejishu");
List<String> resultList = list.stream()
 .filter(it -> it.contains("li"))// 1. 只要包含li的数据
 .filter(it -> it.contains("lige"))// 2. 只要包含lige的数据
 .map(String::toUpperCase)// 3. 对符合的数据作进一步加工,转换大写
 .map(String::toLowerCase)// 4. 对符合的数据作进一步加工,转换小写
 .collect(Collectors.toList());
resultList.forEach(System.out::println);

Whether it is the filter method, the map method, or other methods, we enter the source code level and return a StatelessOp object or a StatefulOp object.

So there is a structure like this:

But what does it have to do with Sink? Let's look at the filter or map source code in turn:

Directly returns an anonymous StatelessOp object and implements the opWrapSink method. The opWrapSink method is to pass in a sink object and return another sink object. And the new sink object has a reference to the incoming sink object.

But, what's the use of this code? When was it triggered?

Don't worry, let's dive into it step by step, starting with the collect(Collectors.toList()) method.

Here we need to know that the terminal object passed into the xx method is ReduceOp, and this ReduceOp object returns an anonymous inner class ReducingSink object when makeSink.

We mentioned makeSink here, returning an anonymous inner class ReducingSink object.

Execute warpSink first, and then execute copyInto. To put it bluntly, first package the sink into a chain sink, and then traverse the sink chain to copy it into the result object. Both steps here are very core.

First look at warpSink:

  1. When entering for the first time, this is the last Stream object, traversing from the tail to the head
  2. Each time it is traversed, a new Stream object is obtained, usually a StatelessOp object or a StatefulOp object
  3. Execute the opWrapSink method of the operation object, which is the anonymous implementation.
  4. In each opWrapSink implementation method, the previous sink is passed in, and finally a sink list is obtained

Finally, return to the head node of the sink chain, which is called the packaged sink internally, named wrapped, and then prepare to execute the begin, forEachRemaining, end methods.

forEachRemaning finally calls the accept method.

Animation to understand the Stream execution process

 

Click Follow to learn about HUAWEI CLOUD's new technologies for the first time~

{{o.name}}
{{m.name}}

Guess you like

Origin my.oschina.net/u/4526289/blog/5572070