Introduction to the
Stream is a new way for Java 8 to work with arrays, collections, and so on. A Stream is like a factory pipeline on which developers can add processing operations to elements.
The resources
- Understand the implementation of Stream in Java8
- JAVA Stream Stream stateful and stateless operations
concept
Lambda
Lambda is a new feature in Java 8 that allows developers to use Lambda for functional programming by writing functional interfaces. A large number of methods in Stream use functional interfaces, so understanding Lambda is fundamental to understanding subsequent Stream code.
Functional interface
/** * a single unimplemented method interface (functional interface) *@FunctionalInterfaceAnnotations constrain an interface to have only one unimplemented method */
@FunctionalInterface // Identifies a functional interface
interface TestFunctionalInterface{
int add(int b);
}
Copy the code
Lambda
// b is the parameter of the interface add method. The number of parameters is the same as that of the interface method
// -> point to the implementation method
// {} interface implementation method
TestFunctionalInterface testFunc = (b)->{
return b+1;
};
Copy the code
The use of Lambda
package com.studyjava.stream;
public class Java8Lambda {
/** * there is only one method in the interface that needs to be implemented, using@FunctionInterfaceAnnotated, this is the functional interface */
@FunctionalInterface
interface TestFunctionalInterface{
int add(int b);
}
public static void main(String[] args){
// Declare variables separately
System.out.println("Use separate declaration variables");
TestFunctionalInterface testFunc = (b)->{ / / using the Lambda
return b+1;
};
int result = testFunc.add(10);
System.out.println("result=>" + result + "\n");
// used in method arguments
System.out.println("Used in method parameters");
result = addOne((b)->{ return b+1; });/ / using the Lambda
System.out.println("result=>" + result + "\n");
// Declare variables to be passed to method arguments
System.out.println("Declare variables passed to method parameters using");
TestFunctionalInterface testFunc2 = (b)->{ / / using the Lambda
return b+1;
};
result = addOne(testFunc2);
System.out.println("result=>" + result + "\n");
}
public static int addOne(TestFunctionalInterface testFunc){
return testFunc.add(10) +1; }}Copy the code
Stream
This forms a Stream, as shown in the figure above. We can see the components of Stream:
- Data source – List
- Flowing elements – Elements in the List
- Intermediate operation – (Map, filter)
- End Operation – (collect)
Sample code corresponding to the image above:
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Java8Stream {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
System.out.println("before list ===>" + list);
List<Integer> results = list.stream()
.map(item -> Integer.parseInt(item)) // Convert String to Integer
.filter(item -> item < 4) // Filter the list of items less than 4
.collect(Collectors.toList()); // Collect results
System.out.println("after list ===>"+ results); }}// The result of the program running
/*
before list ===>[1, 2, 3, 4, 5]
after list ===>[1, 2, 3]
*/
Copy the code
PS: map().filter(); Instead, the element loops through the map method, returns the result, and then loops through the filter. The two methods share a loop.
Equivalent to the following code:
List<Integer> result = new ArrayList<>(); // Collect results for(String item: list){ Integer int = Integer.parseInt(item); // Data conversion if( item > 4) {// Data filteringresult.add(item); }}Copy the code
Just like the pipeline above, the elements in the array are processed first by the Map method and then by the Filter method.
The data source
The data source is the source of the element to be processed in the Stream. Data sources can be collections or arrays.
List<String> list = new ArrayList<>();
list.add("1");
list.add("3");
list.add("2");
list.add("5");
list.add("4");
Stream<String> listStream = list.stream(); //list as the data source
String[] strArr = new String[10];
strArr[0] ="1";
strArr[1] ="2";
strArr[2] ="3";
strArr[3] ="4";
strArr[4] ="5";
Stream<String> arrStream = Arrays.stream(strArr); // Array as data source
Copy the code
In the middle of operation
Intermediate operation: In Stream, the operation to process elements. For example: transform (map), filter (filter), sort (sorted)
Features:
- After the intermediate operation is set, a Stream object is returned. The developer can add the intermediate action again or end the action.
- When an intermediate action is set, no intermediate action is performed.
Intermediate operations are classified as stateful and stateless
Stateful operations are operations that record their state internally while they are being performed.
For example, sorted, which collects all the elements in a list and then sorts them. When the sorting is complete, iterate again for output to the next intermediate operation or end operation
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(3);
list.add(2);
list.add(5);
list.add(4);
System.out.println("before list ===>" + list);
List<Integer> results = list.stream()
.sorted() // Collect first, sort later
.collect(Collectors.toList()); // Collect results
System.out.println("after list ===>" + results);
Copy the code
Stateless operations are the processing of operations that focus only on the current element itself.
For example, map and filter
End of operation
Closing operation: The collection operation of the last element. For example, collect, reduce, and forEach
Features:
- The operation ends without returning a Stream object
- The intermediate action is performed only when the result action is executed
End operations include short-circuit operations and non-short-circuit operations
- Short-circuit operation means that when traversing an element, the system returns the element that meets the condition and does not traverse any more
- Examples: allMatch(), noneMatch(), findFirst(), findAny()
- Non-short-circuiting operations, as opposed to short-circuiting operations, complete the data source traversal.
- For example, forEach(), forEachOrdered(), toArray(), reduce(), collect(), Max (), min(), count()
Parallel computing
Stream is serial by default, and parallel computing is enabled as follows:
List<Integer> results = list.stream()
.parallel() // Enable parallelism
.map(item -> Integer.parseInt(item))
.filter(item -> item < 4)
.collect(Collectors.toList());
Copy the code
Simply call the Parallel method before finishing the operation to start parallelism.
Parallel computing relies on the ForkJoin framework provided by Java7 to split a task into several smaller tasks and then merge the results of the smaller tasks.
Note when using parallelism:
- Avoid using external state in intermediate operations
- Reason: The fundamental problem with multithreaded security is state shared with other threads.
- Parallel execution may result in inconsistent observed state from thread to thread
- Handle primitive type data, using a wrapped Stream. Such as: LongStream, IntStream
- Simple data processing uses serial rather than parallel processing
- ForkJoin takes time to split tasks.
- Multithreaded context switching takes time
Test code:
package com.studyjava.stream;
import com.sun.deploy.util.StringUtils;
import org.junit.Test;
import java.util.*;
import java.util.stream.BaseStream;
import java.util.stream.LongStream;
public class Java8StreamParallel {
/** * In longStream, parallelism is faster than serial */
@Test
public void longStreamParallel(a) {
// The boxing class is computed in parallel
// Parallel computation
long time = System.currentTimeMillis();
long sum1 = LongStream.rangeClosed(1.100000000000l).parallel().map(log -> log * 10).sum();
System.out.println(System.currentTimeMillis() - time);
// serial computation
time = System.currentTimeMillis();
long sum2 = LongStream.rangeClosed(1.100000000000l).map(log -> log * 10).sum();
System.out.println(System.currentTimeMillis() - time);
System.out.println("sum1 = " + sum1 + " sum2 = " + sum2);
}
/** * Simple data processing in a List uses parallel processing slower than serial processing */
@Test
public void singleParallel(a) {
//ArrayList parallel computation
List<Long> arrayList = new LinkedList<>();
for (long i = 0; i < 1000; i++) {
arrayList.add(i);
}
long time = System.currentTimeMillis();
arrayList.parallelStream()
.map(log ->
log * 10
).reduce(0L, Long::sum);
long end = System.currentTimeMillis() - time;
System.out.println("Parallel end time--->" + end);
time = System.currentTimeMillis();
arrayList.stream()
.map(log ->
log * 10
).reduce(0L, Long::sum);
end = System.currentTimeMillis() - time;
System.out.println("Sequential end time--->" + end);
}
/** * Complex data processing in a List uses parallel processing faster than serial processing */
@Test
public void arrayParallel(a) {
//ArrayList parallel computation
List<Long> arrayList = new LinkedList<>();
for (long i = 0; i < 1000; i++) {
arrayList.add(i);
}
long time = System.currentTimeMillis();
arrayList.parallelStream().map(log -> {
try {
// It takes 1ms to simulate complex data processing
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return log * 10;
}).reduce(0L, (i, a) -> i + a);
long end = System.currentTimeMillis() - time;
System.out.println("Parallel end time--->" + end);
time = System.currentTimeMillis();
arrayList.stream().map(log -> {
try {
// It takes 1ms to simulate complex data processing
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return log * 10;
}).reduce(0L, (i, a) -> i + a);
end = System.currentTimeMillis() - time;
System.out.println("Sequential end time--->"+ end); }}Copy the code
The commonly used
The data source
- The base type contains Stream
- IntStream
- LongStream
- DoubleStream
- Collection classes
- List
- Set
- Map
- Array (conversion required)
- Stream.of()
- Internal call to arrays.stream ()
- Arrays.stream()
- Stream.of()
In the middle of operation
- map
- mapToInt
- mapToLong
- mapToDouble
- filter
- sorted
Results the operation
- Collect – Collect results
- Collect (Collectors. ToList) – The result changes to a List
- The forEach – traversal
- The count – count
- Sum-sum (can only be used if converted to LongStream)
The sample
package com.studyjava.stream;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;
public class Java8Stream {
private static long getCurrentTime(a) {
return System.currentTimeMillis();
}
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
/ / collect - collection
System.out.println("Collect");
System.out.println("before list ===>" + list);
List<Integer> intList = list.stream()
.map(item -> Integer.parseInt(item)) // Convert the data type
.filter(item -> item < 4) // Filter data
.collect(Collectors.toList()); // Collect results
System.out.println("after list ===>" + intList + "\n");
/ * the execution result before the list = = = > [1, 2, 3, 4, 5] after the list = = = > [1, 2, 3] * /
/ / count - count
System.out.println("Count - count");
System.out.println("before list ===>" + list);
long count = list.stream()
.map(item -> Integer.parseInt(item)) // Convert the data type
.filter(item -> item < 4) // Filter data
.count(); / / count
System.out.println("count ===>" + count + "\n");
/ * the execution result before the list = = = > [1, 2, 3, 4, 5] count = = = > 3 * /
/ / the sum - sum
System.out.println("Sum - sum");
System.out.println("before list ===>" + list);
long sum = list.stream()
.mapToLong(item -> Long.parseLong(item)) // Convert data type (mapToLong)
.filter(item -> item < 4) // Filter data
.sum(); / / count
System.out.println("sum ===>" + sum + "\n");
/ * the execution result before the list = = = > [1, 2, 3, 4, 5] sum = = = > 6 * /
/ / forEach - traversal
System.out.println("Foreach-traversal");
list.stream()
.mapToLong(item -> Integer.parseInt(item)) // Convert the data type
.filter(item -> item < 4) // Filter data
.forEach(item -> System.out.println(item)); / / traverse
Before list ===>[1, 2, 3, 4, 5] 1 2 3 */}}Copy the code
conclusion
- Enhanced code logic
- Functional programming: Lambda and Stream are used to simplify code and highlight data processing logic
- Chain call: data processing logic is joined to one another, linear logic is consistent with the way of thinking
- Deferred execution: The Stream is executed only when the call terminates the operation. This means that different intermediate operations can be concatenated according to the business logic before the call terminates the operation
- Parallel computing is simple to implement
- Stream blocks the traversal step of the data Stream, allowing the developer to focus on the processing of elements. The realization of parallel computing is entrusted to the underlying code, and the developer only needs to call the parallel computing method provided by the underlying code to enable parallel computing.