Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.
This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.
What is a Stream
1.1 introduction
A new feature has been added to java8: Stream. A Stream, unlike an I/O Stream, is more like a collection class with Iterable, but behaves differently. It is an enhancement to a collection object, allowing developers to work declaratively with data sources (collections, arrays, and so on). It focuses on various efficient aggregate operations and bulk data operations on data sources.
For example, a Stream implicitly iterates internally and converts data if it is given something to do with the elements it contains, such as “filter out strings greater than 10” or “get the first letter of each string.”
Stream is a high-level abstraction of Java collection operations and expressions. The Stream API treats the data source it processes as a Stream, which is transmitted and computed in a Pipeline. The supported operations include filtering, sorting, aggregation, etc., and the final processing result is obtained when it reaches the end point.
A few key concepts:
-
The Stream element is a queue of elements from the data source; the Stream itself does not store elements.
-
Data sources (sources of streams) include collections, arrays, I/O channels, generators, and so on.
-
The aggregation operations are similar to the filter, map, find, match, and sorted operations in SQL
-
The Pipeline operation Stream returns the Stream object itself after the operation in the Pipeline, so that multiple operations are concatenated into a Pipeline and form Fluent style code. This approach optimizes operations, such as delayed execution and short-circuiting.
-
The Stream API implements internal iteration using a Visitor pattern, unlike the previous java8 iteration of collections (external iteration).
-
The Stream API supports both serial (Stream()) and parallel (parallelStream()) operations.
1.2 Stream API features:
-
The use of the Stream API goes hand-in-hand with lambda expressions, also a new java8 feature, to greatly improve coding efficiency and code readability.
-
The Stream API provides serial and parallel operations. The parallel operation can take advantage of the multi-core processor, and the parallel operation can be performed by fork/join to improve the running speed.
-
The Stream API works in parallel to write efficient concurrent programs without writing multithreaded code, and generally avoids multithreaded code errors.
Unlike the previous Collection operation, the Stream operation has two basic characteristics:
- Pipelining: All intermediate operations return the flow object itself. These operations can be cascaded into a pipe, as in fluent style. This allows you to optimize operations, such as delay and short-circuiting.
- Internal iteration: Previously, collections were iterated explicitly outside the collection using either Iterator or for-each. This is called external iteration. A Stream provides a means of iterating internally through a Visitor pattern.
Why Stream? Experience the convenience of functional programming! Reduce your code!
**Stream operations are based on a combination of functional interfaces, which in Java have only one exposed method in the interface. **Java8 provides many functional interfaces, usually declared with the @functionalinterface annotation. Some functional interfaces are necessary.
Create a Stream
Class diagram of stream:
2.1 Through Collection interface functions
The Collection interface in Java8 is extended to provide two methods for retrieving streams:
- Default Stream< E> Stream () : Returns a sequential Stream
List<String> strings = Arrays.asList("test"."lucas"."Hello"."HelloWorld"."Go Wuhan!");
Stream<String> stream = strings.stream();
Copy the code
- Default Stream< E> parallelStream() : returns a parallelStream
List<Employee> list = new ArrayList<>();
Stream<Employee> stream = list.stream();
Stream<Employee> parallelStream = list.parallelStream();
Copy the code
Parallel flow is a multi-threaded mode, which needs to consider thread safety.
2.2 by the Stream
2.2.1 Through Stream: Creates a Stream by value
Stream<String> stream = Stream.of("test"."lucas"."Hello"."HelloWorld"."Go Wuhan!");
Copy the code
2.2.2 Create a Stream through the Stream function
You can use the Stream static methods stream.iterate () and stream.generate () to create an infinite Stream.
- Iterate: public static< T> Stream< T> iterate(final T seed, final UnaryOperator< T> f)
- Public static< T> Stream< T> generate(Supplier< T> s)
Example:
/ / iteration
Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2).limit(2);
stream3.forEach(System.out::println);
System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- --");
/ / generated
Stream<Double> generateA = Stream.generate(new Supplier<Double>() {
@Override
public Double get(a) {
returnjava.lang.Math.random(); }}); Stream<Double> generateB = Stream.generate(()-> java.lang.Math.random()); Stream<Double> generateC = Stream.generate(java.lang.Math::random).limit(4);
generateC.forEach(System.out::println);
// Execution result
0
2
-------------
0.0064617087705397536
0.24943325913078163
0.9396182936441738
0.031970039813425166
Copy the code
This method of function creation is less commonly used, but varies from scenario to scenario. The above example illustrates how important the combination of a Stream and a Lambda expression is. The code is therefore concise and easy to understand.
Common usage of intermediate operation of Stream
There are a lot of middle Stream operation, more intermediate operations can be connected to form a line, between each operation as one of the workers on the assembly line, each worker can be processed convection, after processing the result or a Stream, unless the assembly line to trigger termination of operations, or intermediate operation will not perform any processing! When the operation terminates, it is processed all at once, which is called lazy evaluation.
These intermediate operations are some of the Stream interface uses we often use:
3.1 the filter method
The filter method filters the elements by setting conditions and produces a new stream (that is, you select the elements in the filter stream).
For example, the following code snippet uses the filter method to filter out empty strings:
public static void main(String[] args) {
List<String> strings = Arrays.asList("test"."lucas"."Hello"."HelloWorld"."Go Wuhan!"); strings.stream().filter(string -> ! string.isEmpty()).forEach(System.out::println); }Copy the code
The results
Test Lucas Hello worldCopy the code
3.2 concat method
The concat method concatenates two streams together to compose a single Stream. If both input streams are sorted, the new Stream is sorted. If either of the input streams is parallel, the new Stream is also parallel; When a new Stream is closed, both of the original input streams will be closed.
Example:
Stream.concat(Stream.of(1.4.3), Stream.of(2.5))
.forEach(integer -> System.out.print(integer + ""));
Copy the code
The results
1, 4, 3, 2, 5Copy the code
3.3 the map method
methods | describe |
---|---|
map(Function f) | Take as an argument a function that is applied to each element and mapped to a new element. |
mapToDouble(ToDoubleFunction f) | Take as an argument a function that is applied to each element, producing a new DoubleStream. |
mapToInt(ToIntFunction f) | Receives a function as an argument that is applied to each element, producing a new IntStream. |
mapToLong(ToLongFunction f) | Take as an argument a function that is applied to each element, producing a new LongStream. |
flatMap(Function f) | Take a function as an argument, replace every value in the stream with another stream, and then join all the streams into one stream |
The map method is used to map each element to the corresponding result.
For example, the following code snippet prints the square of an element using a map:
List<String> strings = Arrays.asList("test"."lucas"."Hello"."HelloWorld"."Go Wuhan!"); strings.stream() .filter(string -> ! string.isEmpty()) .map(String::length) .forEach(System.out::println);// Run the result
4
5
5
10
4
Copy the code
Take a look at the map source code:
Function is a new functional interface to java8, and map needs to pass in an implementation of Function. The direct use of Lambda expressions perfectly allows us to focus only on the map () parentheses and omit a lot of verbose interface implementation writing (such as String::length, “::”). Another Java 8 feature “reference methods” is the colon “::” used for method calls.
3.4 flatMap method
The flatMap method is similar to the Map method in that the map method creates a new Stream, and the flatMap () method replaces the elements of the original Stream with the converted Stream.
If the Stream produced by the conversion function is null, it should be replaced by an empty Stream.
FlatMap has three variations of the original type, namely flatMapToInt, flatMapToLong and flatMapToDouble.
Stream.of(1.2.3)
.flatMap(integer -> Stream.of(integer * 10))
.forEach(System.out::println);
// 10,20,30
Copy the code
The expression passed to the flatMap takes a parameter of type Integer and, by multiplying the original element by 10 through the conversion function, generates a stream with only that element, which replaces the elements in the original stream.
Reduce method in 3.5 Stream
The Reduce method has three overloaded methods.
The first method
Accepts a lambada expression of type BinaryOperator. The general application is as follows
Optional<T> reduce(BinaryOperator<T> accumulator);
Copy the code
example
List<Integer> numList = Arrays.asList(1.2.3.4.5);
int result = numList.stream().reduce((a,b) -> a + b ).get();
System.out.println(result);
Copy the code
Second method
The only difference from the first method implementation is that when it is first executed, the expression’s first argument is not the first element of the stream, but is specified by the signature’s first argument identity.
T reduce(T identity, BinaryOperator<T> accumulator);
Copy the code
example
List<Integer> numList = Arrays.asList(1.2.3.4.5);
int result = numList.stream().reduce(0,(a,b) -> a + b );
System.out.println(result);
Copy the code
In fact, the two implementations are almost different, the first one has only one more word than the first to define the initial value. In addition, since the stream is empty, the first implementation does not directly calculate the result of the method. Instead, it wraps the result in Optional. We can obtain a result of type Integer through its GET method, and Integer allows NULL. The second implementation, because it allows specifying an initial value, does not return null even if the stream is empty. When the stream is empty, reduce simply returns the initial value.
The third way
The use of the third method compared with the first two somewhat complex, because the former two implementations have a defect, the results must be the same and the types of elements in stream, such as the following code example, the stream of type int, then the calculation results must also for int, this led to the lack of flexibility, can’t even finish some tasks, We are summing over a series of ints, but the sum is too large for an int and must be upgraded to a long. This third signature is useful and does not tie the execution result to the type of the element in the stream.
<U> U reduce(U identity,
BiFunction<U, ? super T, U> accumulator,
BinaryOperator<U> combiner);
Copy the code
example
List<Integer> numList = Arrays.asList(1.2.3.4.5.6);
ArrayList<String> result = numList.stream().reduce(new ArrayList<String>(), (a, b) -> {
a.add("An int to a string." + Integer.toString(b));
return a;
}, (a, b) -> null);
System.out.println(result);
//[int converts to string:1, int converts to string:2, int converts to string:3, int converts to string:4, int converts to string:5, int converts to string:6]
Copy the code
If you are using parallelStream Reduce for concurrent operations, in order to avoid contention, each Reduce thread will have a separate result. Combiner is used to combine the results of each thread to get the final result.
The BinaryOperator is intended for multithreading, and if you do not declare multithreading in the Stream, you will not use the subtask and will not call this method. In addition, when using the BinaryOperator in multiple threads, there are thread safety concerns.
3.6 method of peek
The peek method generates a new Stream that contains all the elements of the original Stream and provides an instance of a Consumer function. Each element of the new Stream is consumed before the given Consumer function is executed. See Consumer.
Stream.of(1.2.3)
.peek(integer -> System.out.println("Preferred consumption function in Peek: This is" + integer))
.forEach(System.out::println);
Copy the code
Running results:
Priority consumption function in PEEK: This is the priority consumption function in peek 1 1: this is the priority consumption function in peek 2 2: this is the priority consumption function in peek 3 3Copy the code
3.7 limit/skip method
Limit returns the first n elements of the Stream;
Skip is throwing away the first n elements.
For example, the following code snippet factoring four elements using the limit method:
List<Integer> numbers = Arrays.asList(1.2.3.4.5);
numbers.stream().limit(4).forEach(System.out::println);
/ / 1, 2, 3, 4
Copy the code
3.8 sorted method
The sorted method is used to sort convection.
For example, the following code snippet sorts using the sorted method:
List<Integer> numbers = Arrays.asList(3.2.2.3.7.3.5);
numbers.stream().sorted().forEach(System.out::println);
/ / 2,2,3,3,3,5,7
Copy the code
3.9 a distinct method
Distinct is mainly used to remove weights.
For example, the following code snippet uses distinct to de-duplicate elements:
List<Integer> numbers = Arrays.asList(3.3.7.3.5);
numbers.stream().distinct().forEach(System.out::println);
/ / 3,7,5
Copy the code
3.10 A small summary instance
List<String> strings = Arrays.asList(""."lucas"."Hello"."HelloWorld"."Go Wuhan!");
Stream<Integer> distinct = strings.stream()
.filter(string -> string.length() <= 6) // filter out "HelloWorld"
.map(String::length) // Change Stream from String to int
.sorted() //int from small to large
.limit(2) // Select the first two
.distinct(); / / to heavy
distinct.forEach( System.out::println);
Copy the code
Running results:
0
4
Copy the code
Four Stream final operation
Terminating operations generate results from the pipeline of a stream. The result can be any value that is not a stream, such as List, Integer, and so on. The intermediate Stream operation returns a Stream. How do we convert a Stream to the desired type? And that requires terminal operation
The final operation consumes the flow, producing an end result. That is, the flow cannot be used again after the final operation, nor can any intermediate operation be used, or an exception will be thrown:
java.lang.IllegalStateException: stream has already been operated upon or closed
Copy the code
Common final operations are as follows:
methods | describe |
---|---|
allMatch(Predicate p) | Check that all elements match |
anyMatch(Predicate p) | Check that at least one element matches |
noneMatch(Predicate p) | Check that all elements are not matched |
findFirst() | Returns the first element |
findAny() | Returns any element in the current stream |
count() | Returns the total number of elements in the stream |
max(Comparator c) | Returns the maximum value in the stream |
min(Comparator c) | Returns the minimum value in the stream |
forEach(Consumer c) | Internal iteration (Using the Collection interface requires the user to do iteration, called external iteration. Instead, the Stream API uses internal iteration — it does the iteration for you.) |
4.1 the forEach method
Stream provides the method ‘forEach’ to iterate over each data in the Stream. Note that the argument to the forEach method is Cousumer.
The following code snippet prints 10 random numbers using forEach:
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);
Copy the code
4.2 the count method
Count is used to count the number of elements in the stream.
List<String> strings = Arrays.asList(""."lucas"."Hello"."HelloWorld"."Go Wuhan!");
long count = strings.stream()
.filter(string -> string.length() <= 6)
.map(String::length)
.sorted()
.limit(2)
.distinct()
.count();
System.out.println(count);
/ / 2
Copy the code
4.3 collect method
There are many static methods in collect, and flexible use of them will play a great role.
Collect is a reduction operation that accepts various practices as arguments to accumulate elements in a stream into a summary result:
List<String> strings = Arrays.asList(""."lucas"."Hello"."HelloWorld"."Go Wuhan!");
List<Integer> collect = strings
.stream()
.filter(string -> string.length() <= 6)
.map(String::length)
.sorted()
.limit(2)
.distinct()
.collect(Collectors.toList());
collect.forEach(System.out::println);
// Run the result
0
4
Copy the code
As above, you end up with a List array, which is where the stream ends up.
The preceding example is only a simple List generation example. Collectors provides more convenient operations for developers to use. You can use various static methods provided by the Collectors class to realize diversified requirements.
Some common static methods provided by the Collectors class:
methods | The return type | role | The instance |
---|---|---|---|
toList | List | Collect elements from the stream into the List | List emps= list.stream().collect(Collectors.toList()); |
toSet | Set | Collect elements from the stream into a Set | Set emps= list.stream().collect(Collectors.toSet()); |
toCollection | Collection | Collect elements from the stream into the created collection | Collectionemps=list.stream().collect(Collectors.toCollection(ArrayList::new)); |
counting | Long | Count the number of elements in the stream | long count = list.stream().collect(Collectors.counting()); |
summingInt | Integer | Sum of integer attributes of elements in the flow | inttotal=list.stream().collect(Collectors.summingInt(Employee::getSalary)); |
joining | String | Concatenate each string in the stream | String str= list.stream().map(Employee::getName).collect(Collectors.joining()); |
groupingBy | Map<K, List> | According to a certain attribute value, the convection is grouped, the genus is K, and the result is V | Map<Emp.Status, List> map= list.stream() .collect(Collectors.groupingBy(Employee::getStatus)); |
partitioningBy | Map<Boolean, List> | Partition according to true or false | Map<Boolean,List>vd= list.stream().collect(Collectors.partitioningBy(Employee::getManage)); |
reducing | The type of reduction produced | Starting with an initial value that acts as an accumulator, the BinaryOperator is combined with elements in the stream one by one to reduce to a single value | inttotal=list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum)); |
averagingInt | Double | Calculates the average value of the Integer attribute of the element in the stream | doubleavg= list.stream().collect(Collectors.averagingInt(Employee::getSalary)); |
maxBy | Optional | Select the maximum value based on the comparator | Optionalmax= list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary))); |
minBy | Optional | Select the minimum value based on the comparator | Optional min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary))); |
summarizingInt | IntSummaryStatistics | Collect statistics for the Integer attribute in the stream. For example, average value IntSummaryStatisticsiss= | list.stream().collect(Collectors.summarizingInt(Employee::getSalary)); |
collectingAndThen | Converts the type returned by the function | Wrap another collector, and execute a conversion function on it | inthow= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size)); |
Here’s an example of grouping and multilevel grouping:
List<Person> person_list = Arrays.asList(
new Person("lucas".26),
new Person("lucas".25),
new Person("lucas".60),
new Person( "chris".27),
new Person( "chris".20),
new Person("theo".52)); System.out.println("-------- Primary grouping --------- using the Collectors static method");
Map<String, List<Person>> group = person_list.stream()
.collect(Collectors.groupingBy(Person::getName));
System.out.println(group);
System.out.println("-------- Secondary grouping, using a static method, ---------");
Map<String, Map<String, List<Person>>> group2 = person_list.stream()
.collect(Collectors.groupingBy(Person::getName, Collectors.groupingBy((people) -> {
if (people.getAge() < 30) return "Youth";
else if (people.getAge() < 50) return "Middle-aged";
else return "Old age";
})));
System.out.println(group);
Copy the code
Running results:
-------- Use the static method to group Collectors --------- {Chris =[cn.lucas.Person@52cc8049, cn.lucas.Person@5b6f7412], lucas=[cn.lucas.Person@27973e9b, cn.lucas.Person@312b1dae, cn.lucas.Person@7530d0a], Theo =[cn.lucas.Person@27bc2616]} -------- Use the static method to group Collectors: --------- {Chris ={young =[cn.lucas.Person@52cc8049, cn.lucas.Person@5b6f7412]}, Lucas ={youth =[cn.lucas.Person@27973e9b, cn.lucas.Person@312b1dae], Old =[cn.lucas.Person@7530d0a]}, theo={old =[cn.lucas.Person@27bc2616]}}Copy the code
It can be seen that the method in Collect can be skillfully used to realize how convenient and elegant some “dirty work, tired work and work without much value” (the above logic can be directly used in the List data processing process of a pile of data secondary List display). We need to focus, and the bulk of the code is on the business logic of the code.
One more Collectors. PartitioningBy segmentation examples of data blocks, they will make you feel simple and elegant:
public static void main(String[] args) {
Map<Boolean, List<Integer>> collectParti = Stream.of(1.2.3.4)
.collect(Collectors.partitioningBy(it -> it % 2= =0));
System.out.println("Split data block:" + collectParti);
}
// Split data block: {false=[1, 3], true=[2, 4]}
Copy the code
4.4 findFirst/anyMatch/Max/min, and other methods
List<Person> person_list = Arrays.asList(
new Person("lucas".26),
new Person("maggie".25),
new Person("queen".23),
new Person( "chris".27),
new Person( "max".29),
new Person("theo".30)); System.out.println("-------- allMatch() checks if all elements match ---------");
boolean allMatch = person_list.stream()
.allMatch((person -> person.getName().equals("lucas")));
System.out.println(allMatch);
System.out.println("-------- anyMatch() checks if at least one element is matched ---------");
boolean anyMatch = person_list.stream()
.anyMatch(person -> person.getName().equals("lucas"));
System.out.println(anyMatch);
System.out.println("-------- noneMatch checks if all elements are not matched ---------");
boolean noneMatch = person_list.stream()
.noneMatch(person -> person.getName().equals("lucas"));
System.out.println(noneMatch);
System.out.println("-------- findFirst() returns the first element ---------");
Optional<String> first = person_list.stream()
.map(Person::getName)
.findFirst(); // Get the first element
System.out.println(first.get());
System.out.println("-------- findAny() returns any element in the current stream ---------");
boolean anyMatch1 = person_list.stream()
.anyMatch(person -> person.getName().equals("lucas"));
System.out.println(anyMatch1);
System.out.println("-------- Max () returns the maximum value in the stream ---------");
Optional<Integer> intOptional = person_list.stream()
.map(Person::getAge)
.max(Integer::compare); / / Max
System.out.println(intOptional.get());
Copy the code
Running results:
-- -- -- -- -- -- -- -- allMatch () to check whether all matched elements -- -- -- -- -- -- -- -- -- false -- -- -- -- -- -- -- -- anyMatch () check whether the matching at least one element -- -- -- -- -- -- -- -- - true -- -- -- -- -- -- -- -- noneMatch Check whether there is no matching all elements -- -- -- -- -- -- -- -- -- false -- -- -- -- -- -- -- -- findFirst () returns the first element -- -- -- -- -- -- -- -- -- Lucas -- -- -- -- -- -- -- -- findAny () returns the current flow of any element -- -- -- -- -- -- -- -- - true -------- Max () returns the maximum value in the stream --------- 30Copy the code
reference
Juejin. Im/post / 5 df4a9…
www.jianshu.com/p/2b40fd076…
Blog.csdn.net/liudongdong…
www.jianshu.com/p/e6cdc48bb…
Docs.oracle.com/javase/8/do…
www.jianshu.com/p/71bee9a62…
Blog.csdn.net/u012706811/…
www.fulinlin.com/2019/07/25/…