Detailed explanation of the cooperative use of stream and functional interface in Java 8

preface

Java 8 provides a set of APIs called stream for processing traversable streaming data. The design of stream API fully integrates the concept of functional programming and greatly simplifies the amount of code.

In fact, you can regard stream as an advanced version of iterator. In the original version of iterator, users can only traverse elements one by one and perform some operations on them; In the advanced version of stream, users only need to give what operations they need to perform on the elements contained, such as "filter out strings with a length greater than 10", "get the first letter of each string", etc. How to apply these operations to each element, just give it to stream! (I don't tell ordinary people this secret script:)

Let's explain how to use the commonly used stream API with the corresponding functional interface to achieve the purpose of data processing.

Similar to the three major problems that have plagued philosophers for thousands of years, we also have three mysteries about stream to solve: where does it come from? What can it do? What will it become?

Generate and supplier

The most common source of stream is collection. A collection is an abstract container for a set of traversable elements. It has two major class implementations: set that does not allow repeating elements and list that allows repeating elements. Just add after a collection object Stream () or Parallelstream () can get the corresponding stream.

If there is no ready-made collection, or the collection is too large to store, is there any way to generate a stream? If you know the algorithm of generating each element in the stream, you can create a stream out of nothing. The method used here is stream Generate(), which relies on a functional interface supplier.

The supplier's method get () returns a t object every time it is called. Because the get () method does not receive any parameters, when using generate, the code will always be written like () - > returnValue.

In addition, because get () can be called infinitely many times, the stream generated by generate is also infinitely long. If necessary, it can be called by Limit() intercepts the first few elements.

For example, if you want to obtain an Infinite Random UUID sequence, you can use the following method:

It is also feasible to obtain sequences such as 1 ~ 10, but a helper class is required to record the current state. No case is provided here.

Foreach and consumer

When you know how to generate a stream, you also need to know how to consume it. Since a stream can come from a collection, it should eventually become a collection. This is the credit of collect(). Collect() receives a collector as a parameter and returns the collection object generated from stream. However, this collector is not a functional interface, so it does not belong to the focus of this article. The following focuses on the foreach method.

Foreach works with the functional interface consumer. The void accept (T) method of consumer is to consume each element in the stream. Because accept takes a single element t as a parameter, foreach will be written in the form of E - > statement, where the statement does not return any value.

For example, if you print each element in the stream line by line, you can write:

Or further simplified by method reference:

Reduce and binaryoperator

In addition to foreach, a terminal operation that devours elements, there are two common patterns for using elements in stream. The first is still a terminal operation: integrate all elements and finally return a single value. We call this operation reduce. The second is procedural operation, which allows each element to have its own corresponding return value, and then reorganizes it into a new stream for further utilization in the next step. We call the second operation map. Combining the two operations mentioned just now is the famous MapReduce (error).

Reduce is used with a special functional interface called binaryoperator. The prototype of binaryoperator < T > is bifunction < T, t, t >? Originally, bifunction < T, u, R > is a broad functional interface. Its method r apply (T, u, U) accepts two parameters of type T and u and returns a value of type R. If the three types of t u r are the same, you can write bifunction < T, t >. Because this usage is particularly common, it has its own name, binaryoperator < T >. The most common binary operator is binary arithmetic operation, and the well-known addition, subtraction, multiplication and division all belong to this category.

The most common example of explaining reduce is to find the sum of all elements in a stream:

We can see that the characteristics of the reduce method are (a, b) - > returnValue. It returns optional, which we can use Ispresent() to check whether it is null; When the value is not empty, use Get() to get the data.

Map and function

Map is perhaps the most widely used operation in stream. Unlike the bifunction involved in reduce, the functional interface used with map is a slightly simpler function. It is also a broad functional interface, and it is also the most famous representative of functional interface. The method r apply (T, t) of function < T, R > accepts a parameter of type T and returns a value of type R. What map does is to apply this function to each element in the stream to get a new stream composed of R.

For example, make every string in a stream uppercase:

The map method is characterized by E - > returnValue. Just like the system we used before Like out:: println, you can also use method reference to simplify code here, as long as the referenced method meets the expected type of map: pass in a t parameter and return an R value.

Filter and predicate

After introducing the heavyweight operations of foreach, reduce and map, let's deal with an embarrassing question: what if there are elements we don't want in the stream? The answer is to use the filter to kick them out.

The functional interface used with filter is predict. The Boolean test (T) method of predict < T > accepts a parameter of type T and returns true or false. We can think that predicate < T > is a specific function < T, Boolean >, because it is widely used, so the independent portal becomes a set of separate interfaces.

In the following example, the program prints only even numbers in the stream:

It can be seen that because predicate is a specific function, the characteristics of the filter method are the same as that of map in appearance. However, the filter should ensure that the returnValue in E - > returnValue is a boolean, otherwise the compilation will report an error.

Sorted and comparator

Finally, let's look at the very powerful sorted method in stream, which allows us to customize the comparison rules to sort the elements in stream. The functional interface matched with sorted is comparator. Comparator < T > uses the int compare (t O1, t O2) method to compare two T-type objects. Sorting is achieved by comparing the relative sizes of objects.

The following example arranges the floating-point numbers in stream in ascending order of absolute values and prints them:

It is not difficult to see that the characteristics of the sorted method are similar to those of reduce. They all have the structure of (a, b) - > returnValue, but ensure that the returnValue is of type int.

summary

The above is the whole content of this article. I hope the content of this article has a certain reference value for your study or work. If you have any questions, you can leave a message. Thank you for your support for programming tips.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>