The lambda expression is described earlier, but we can see that the lambda expression actually simplifies the writing of a part of the code and is not a very useful language feature. But if the lambda expression fits into the Stream class library described in this article, it will play a huge role. initial knowledge of flow class library
As usual, let's take a look at an example. There is a list of integers, and I would now like to find all of them greater than 5, so I might write that. Although this is a code, but even if the implementation of such a simple function, also need such a large piece of code, it is really unpleasant.
list<integer> integers = new arraylist<> ();
Integers.addall (Arrays.aslist (1, 2, 3, 4, 5, 6, 7, 8));
Gets all elements greater than 5
list<integer> integersGreaterThan5 = new arraylist<> ();
for (int i:integers) {
if (i > 5) {
integersgreaterthan5.add (i);
}
}
System.out.println (INTEGERSGREATERTHAN5);
So what if it fits into the Stream class library. The code is greatly reduced and the readability is greatly improved.
Integersgreaterthan5.clear ();
Use Stream class Library
integersGreaterThan5 = Integers.stream ()
. Filter (i-> i > 5)
. Collect (Collectors.tolist ());
System.out.println (INTEGERSGREATERTHAN5);
The streaming class library is a new set of class libraries in Java 8 that allow us to perform complex operations on the collection class libraries, which are located under the Java.util.stream package and are not confused with the Java IO stream. From the above code you can see that the use of the Stream class library can basically be divided into the following steps: The conversion of the collection to flow, convection operation, the flow into the corresponding data structure. Get Stream
Trace the stream () method of the above code in the IDE that supports viewing the source code. It can be found that in the Java.util.Collection interface, most collection classes implement this interface, which means that most collection classes have this method, and by using this method, we can convert the set to a stream, and almost all of the Stream class libraries need to operate on the stream.
Of course, if you take a look at it, this is the way it looks. This is also a new feature of Java 8, because the Stream class library is a new method of adding to the interface, which is not implemented by Java 8 before. For older versions of the code to work, Java 8 introduced the interface default method, so that the interface can also implement methods, if not implemented in the implementation class, will use the default implementation in the interface. This way, even if the old version of the code does not implement these new interfaces, the program can still work correctly.
Default Stream<e> Stream () {return
Streamsupport.stream (Spliterator (), false);
Stream Operation
Filtration
The most common stream operation is filtering, filtering the equivalent of a Where in SQL, which is to filter the elements in the stream to form a new stream. The result of most stream operations is still a stream, so we can call it by chain. Here is an example of finding an even number greater than 3.
list<integer> integers = stream.of (1, 2, 3, 4, 5, 6, 7, 8, 9, ten)
. Filter (i-> i > 3)
. Filter (i-> I% 2 = 0)
. Collect (Collectors.tolist ());
System.out.println (integers);
Mapping
Another common stream operation is a mapping, similar to a select in SQL, that converts a set of elements to another element. The following example converts a set of integers to squares. This is a simple example of a group of object streams that often need to be converted to another group of objects in practical situations.
list<integer> integers = stream.of (1, 2, 3, 4, 5)
. Map (i-> i * i)
collect (Collectors.tolist ());
System.out.println (integers);
Flat Mappings
Sometimes you need to combine the results of multiple streams into one stream, and you need to use a flat map.
list<integer> integers = Stream.of (arrays.aslist (1, 2), Arrays.aslist (3, 4))
. Flatmap (n-> n.stream ())
. Collect (Collectors.tolist ());
System.out.println (integers);
maximum value and minimum value
These two features need not be said. It should be noted that the Min and Max methods accept lambda expressions in the comparator form, and of course you can use the comparer method described in the previous article to simplify the coding of the comparison.
int min = stream.of (1, 2, 3, 4)
. Min (Comparator.comparingint (i-> i)). get ();
int max = Stream.of (1, 2, 3, 4)
. Max (Comparator.comparingint (i-> i)). get ();
System.out.println (String.Format ("max:%d,min:%d", Max, Min));
Universal Iterations
Sometimes a more complex operation is required: Take the first two elements from one stream to perform an operation, and then proceed with the result and the third element until all the elements are processed. This operation is called reduce. The following example is simple, summation and quadrature.
There are two forms of reduce, the first of which is to take the first two element operations, then to manipulate the result and the third element, and so on. The second is to operate with the given initial value and the first element, and then the result and the second element. Note that the first form of the return value is a optional object, and we need to call the Get () method for the final value.
int sum = Stream.of (1, 2, 3, 4, 5)
. Reduce ((ACC, E)-> ACC + e)
. get ();
System.out.println (String.Format ("sum:%d", sum));
int product = Stream.of (1, 2, 3, 4, 5)
. Reduce (1, ACC, E)-> ACC * e);
System.out.println (String.Format ("product:%d", product);
predicate Actions
Some of the flow operations are similar to the predicate operations in SQL, and can be used to achieve some judgment functions. Allmatch returns True when all elements meet a condition True;anymatch as long as one element is satisfied, Nonematch returns True when no element satisfies the condition, and distinct removes duplicate elements from the stream.
Boolean allGreaterThan5 = Stream.of (1, 2, 3, 4, 5)
. Allmatch (i-> i > 5);
System.out.println ("AllGreaterThan5:" + allGreaterThan5);
Boolean anyEqualsTo2 = Stream.of (1, 2, 3, 4, 5)
. AnyMatch (i-> i = = 2);
System.out.println ("AnyEqualsTo2:" + anyEqualsTo2);
Boolean noneLessThan0 = Stream.of (1, 2, 3, 4)
. Nonematch (i-> i < 0);
System.out.println ("NoneLessThan0:" + noneLessThan0);
list<integer> distinct = Stream.of (1, 1, 2, 3, 2, 4, 5)
. DISTINCT ()
. Collect (Collectors.tolist ());
There are also operations that can intercept a part of a stream, and limit retains the first few elements of the stream, while skip skips the first few elements and gets all the elements after it.
String List1 = Stream.of (1, 2, 3, 4, 5)
. Limit (3).
map (string::valueof)
. Collect (Collectors.joining (",", ",") [", "]"));
System.out.println (list1);
String List2 = Stream.of (1, 2, 3, 4, 5)
. Skip (3)
. Map (string::valueof)
. Collect (Collectors.joining (",", "[ ", "]"));
System.out.println (LIST2);
Basic Type Flow
Stream Class Library is a common framework, so it is obvious to use Java generics technology. But we know that because Java has a basic type of boxed unboxing process, there is a performance overhead. To avoid these costs, the Stream class library has special processing for common basic types int, long, double, and prepares them for separate classes and methods.
Intstream Intstream = Intstream.of (1, 2, 3, 4, 5);
Longstream Longstream = Longstream.of (1, 2, 3, 4);
Doublestream Doublestream = Doublestream.of (1.0, 2.0);
For some methods that also have a base type version, you can convert an object stream to the corresponding base type, which is named by the method name +to+ basic type. You might consider using these methods if you need to work with the basic type stream of large amounts of data.
int sum = Stream.of (1, 2, 3, 4)
. Maptoint (i-> i)
. Reduce (0, (ACC, E)-> ACC + e);
System.out.println (String.Format ("sum:%d", sum));
Collecting Device
The last step in using the Stream class library is to convert the stream to the collection we need, which requires a collector. The last step in collecting data requires calling the Collect method, whose arguments are static methods of the Java.util.stream.Collector class. Sounds strange, actually, the return value of these methods is accepted. For example, the ToList () method actually returns the Collector object, which is then processed by the Collect method to get the final collection.
public static <T>
collector<t,?, List<t>> ToList () {return
new collectorimpl<> ( supplier<list<t>>) arraylist::new, List::add,
(left, right)-> {left.addall (right);
ch_id);
}
Get the collection
If you need to convert a stream to a collection, you can use methods such as ToList () and Toset (). They return the list and set collection. If you need to use a specific collection, you can use the Tocollection method, whose arguments are a more specific function interface for creating collections. So here you can create the collection directly using the constructor reference.
list<integer> integers = stream.of (1, 2, 3, 4)
. Collect (Collectors.tolist ());
set<integer> set = Stream.of (1, 2, 3)
. Collect (Collectors.toset ());
Use your own desired set of
arraylist<integer> integers2 = Stream.of (1, 2, 3)
. Collect (Collectors.tocollection ( Arraylist::new));
Get Value
The collector can not only get the collection, but also get a value from the stream, which can be achieved by invoking the Maxby, Minby, Averagexxx, and Summingxxx methods. Here is a set of examples.
int max = Stream.of (1, 2, 3, 4)
. Collect (Collectors.maxby (comparator.comparing (i-> i)). Get
();
int min = stream.of (1, 2, 3, 4)
. Collect (Collectors.minby (comparator.comparing (i-> i))
Double average = stream.of (1, 2, 3, 4)
. Collect (Collectors.averagingdouble (Integer::d oublevalue));
int sum = Stream.of (1, 2, 3, 4)
. Collect (Collectors.summingint (i-> i));
System.out.println (
String.Format ("max:%d,min:%d,average:%f,sum:%d", Max, Min, average, sum));
Sometimes it is necessary to combine the data of a stream into a string, which requires the joining collector, whose three parameters are delimiters, prefixes, and suffixes, respectively. Of course, because it requires a sequence of characters, it is also necessary to use the Map method to convert the integer stream to a string stream.
String string = Stream.of (1, 2, 3, 4, 5)
. Map (string::valueof)
. Collect (Collectors.joining (",", "[", "]")); C13/>system.out.println (string);
Results: [1, 2, 3, 4, 5]
There is also a simple collector, the role is to count, it is important to note that the return of the count is the result of a long type on the line. The main function of this collector is to perform complex functions with other collectors.
Long Count = Stream.of (1, 2, 3)
. Collect (Collectors.counting ());
System.out.println ("Count:" + count);
Results: 3
Data chunking
The data chunking allows you to give a condition, and then the collector will divide the stream into two parts satisfying the condition and not satisfying the condition according to this condition, and the return result of this collector is a map<boolean, list<t>>. The following example divides the stream into odd and even two parts.
Map<boolean, list<integer>> map = Stream.of (1, 2, 3, 4, 5)
. Collect (Collectors.partitioningby (i-> i% 2 = 0));
SYSTEM.OUT.PRINTLN (map);
Results
//{false=[1, 3, 5], true=[2, 4]}
Data Grouping
Data chunking can only be divided into two cases of true and false, if need more subdivision, need to use data grouping. This is probably similar to the group by statement in SQL. The following example divides the stream into several groups according to the number of digits in the array.
Map<integer, list<integer>> map = Stream.of (
.) Collect (Collectors.groupingby (i) (i -> i));
SYSTEM.OUT.PRINTLN (map);
Results
//{1=[21, 11], 2=[32, 22], 3=[43, 33], 4=[54]}
Combination Collector
If the problem is more complex, you can combine multiple collectors, some with overloaded versions, and a second collector that you can use to implement this functionality. Take the previous data grouping example, this time I want to not only group, and only need 10 digits per group, so you can write. The second parameter of the Groupingby can use the mapping collector, mapping the action and the map of the stream operation here, mapping the stream again, and then collecting the result as the key value pair of the last map.
Grouped by single-digit digits, and then only get 10-digit
Map<integer, list<integer>> Map = Stream.of (.),
Collect (Collectors.groupingby (i-> i%,
collectors.mapping (i-> I/10, Collectors.tolist ())));
SYSTEM.OUT.PRINTLN (map);
{1=[2, 1], 2=[3, 2], 3=[4, 3], 4=[5]}
To give another example, now I want to evaluate the results of each group. You can use a different collector summingxxx. There are also a number of collectors can complete the operation of averaging, and so on, here is not enumerated.
According to the number of single-digit groups, and then seek the group and
Map<integer, integer> map2 = Stream.of (,,,,, and,)
. Collect ( Collectors.groupingby (i-> i%,
collectors.summingint (i-> i)));
System.out.println (MAP2);
{1=32, 2=54, 3=76, 4=54}
another piece of content
There is a little bit of knowledge about how to say, simply put it all right here. Lazy Evaluation
Stream Class library design is very sophisticated, but also a lot of performance optimization. All stream operations are inert, meaning that the entire stream operation does not begin until the collector is finally invoked. Before that, your streaming operation was nothing more than a SQL execution plan, and there was no real execution of the program. So the following code will not output anything.
Stream.of (1, 2, 3, 4, 5)
. Filter (i-> i > 1)
. Filter (i-> {
System.out.print (i);
return i <= 3;
});
Single Cycle
If a lot of flow operation, the Flow class library will be convection for many iterations, resulting in slow program speed. This worry is also redundant, the Stream class library is optimized, will ensure that the iteration at least the number of times. So the following code output is 122334455, which indicates that the stream has only been iterated once.
list<integer> integers = stream.of (1, 2, 3, 4, 5)
. Filter (i-> {
System.out.print (i);
return i > 1;
})
. Filter (i-> {
System.out.print (i));
return i <= 3;
})
. Collect (Collectors.tolist ());
System.out.println ();
New for Loop
Originally, if we need to do a certain number of loops, we need to use for to do.
The traditional for loop for
(int i = 0; i < 3; ++i) {
System.out.print (i);
}
System.out.println ();
Now we can simplify this by using the Range method of the Stream class library.
Intstream.range (0, 3)
. ForEach (i-> System.out.print (i));
System.out.println ();
parallelization of Data
Because of the characteristics of the Stream class library, it is very easy to make the stream parallel, and only need to invoke the Parrallel method. The segmentation of the data and the merging of the results will be automatically completed by the class library.
list<integer> integers = intstream.range (1,).
parallel ()
. Filter (i-> i% 2 = 0)
. Boxed ()
. Collect (Collectors.tolist ());
Note that it does not mean that after parallelization, the speed must be faster than the serialization, which requires a comprehensive assessment based on the current system, machine, and the size of the data stream being executed. ArrayList this kind of container is more easy to parallel, and HashMap parallelization is more difficult.
In short, the topic of parallelization is more complex and is not discussed in detail here.
Finally, I recommend a book on Java functional programming, this book for Java 8 functional programming has done a lot of introduction, I feel very good.