Collection data is frequently manipulated in programming. Using Stream in Java8 to process collections, combined with Lambda functional programming, can greatly simplify code. Reasonable use of Stream can improve code readability. On the other hand, since the advent of Java8, the Stream API has been tested by numerous projects, its stability and performance need not be said. There are many related performance test cases on the web to refer to. If someone says to you: Lambda is not readable, high maintenance costs and some other issues, you can rest assured, please take a look at the last point of attention
1. Stream Creation
There are many ways to create Stream. Here are several common ways to create Stream. Lists below use the API of google guava to code directly:
// Mode 1: Stream.of and other static methods, commonly used by tests Stream<String> stream1 = Stream.of("A", "B"); // Mode 2: Collection mode, common such as List, Set Stream<String> stream2 = Lists.newArrayList("A", "B").stream(); Stream<String> stream3 = Sets.newHashSet("A", "B").stream(); // Mode 3: Array mode Stream<String> stream4 = Arrays.stream(new String[]{"A", "B"}); // Mode 4: Create through API interface, file API, etc. Stream<String> stream5 = Files.lines(Paths.get("/file.text")); // Mode 5: Create Stream corresponding to the basic data type, such as IntStream, LongStream, DoubleStream IntStream stream6 = Arrays.stream(new int[] { 1, 2, 3 }); // Mode 6: Through Stream.builder creation Stream<Object> stream7 = Stream.builder().add("A").build();
Modes 2 and 3 above are commonly used. Among them, mode 3 can also use parallelStream to create parallel streams. Other methods can be converted to parallel streams by parallel method to improve data processing efficiency when there is a large amount of data, as follows:
// Create directly using parallelStream Stream<String> stream1 = Lists.newArrayList("A", "B").parallelStream(); // Converting ordinary streams to parallel streams using parallel Stream<String> stream2 = Arrays.stream(new String[]{"A", "B"}).parallel();
2. Stream Intermediate Operation
Stream.map
After processing the original data, new data will be generated, in which the mapToInt, mapToLong, mapToDouble methods can be directly converted to IntStream, LongStream, DoubleStream (less used, you can find it yourself)
// Original Data Add Suffix-N List<String> result1 = Lists.newArrayList("A") .stream().map(item -> item + "-N").collect(Collectors.toList()); // Convert original string to array List<String[]> result2 = Lists.newArrayList("A") .stream().map(item -> new String[]{item}).collect(Collectors.toList());
Stream.flatMap
Merge multiple Streams into one Stream, often used to merge multiple List data
List<String> result = Lists.newArrayList( Lists.newArrayList("A"), Lists.newArrayList("B") ).stream().flatMap(Collection::stream).collect(Collectors.toList());
Stream.filter
Element filtering, which replaces if criteria in a loop, takes a logical expression as a parameter
List<String> result = Lists.newArrayList("A", "B") .stream().filter("A"::equals).collect(Collectors.toList());
Stream.distinct
Element to duplicate, commonly used in simple data types, if an object can be duplicated using TreeSet Example below
// Simple data type deweighting List<String> result1 = Lists.newArrayList("A", "A", "B") .stream().distinct().collect(Collectors.toList()); // Object Data Reduplication List<Demo> result2 = Lists.newArrayList(new Demo()).stream().collect( Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(comparing(Demo::getName))), ArrayList::new) );
@Data class Demo { private String name; private String age; }
Stream.peek
Processing data without changing the original data type differs from map in that peek accepts an operation with no return value and is generally used to modify the internal elements of an object
List<Demo> result = Lists.newArrayList(new Demo()) .stream().peek(item -> item.setName("A")).collect(Collectors.toList());
Stream.sorted
Sort data, support positive and reverse order, and support object type data sorting
// Simple data type sorting List<String> result1 = Lists.newArrayList("A", "B") .stream().sorted().collect(Collectors.toList()); // Object types are sorted by an attribute, in positive order by default, in reverse order using the reversed method List<Demo> result2 = Lists.newArrayList(new Demo()) .stream().sorted(Comparator.comparing(Demo::getName).reversed()).collect(Collectors.toList());
Stream.limit
Limit the number of final output data, intercept elements in the stream, and do not intercept by default
List<String> result1 = Lists.newArrayList("A", "B") .stream().limit(1).collect(Collectors.toList());
Stream.skip
How many elements are skipped before, similar to a limit, which is to intercept a stream and return it immediately when the limit is reached
List<String> result = Lists.newArrayList("A", "B") .stream().skip(1).collect(Collectors.toList());
3. Stream terminate operation
collect
Collectors.toList (collected as List), Collectors.joining (Collect and Stitch as String)
// Collect data as List List<String> result1 = Lists.newArrayList("A", "B").stream().collect(Collectors.toList()); // Collection data is String, default no delimiter, delimiter can be specified using joining method with parameters String result2 = Lists.newArrayList("A", "B").stream().collect(Collectors.joining());
reduce
The data is aggregated into a single value, and the data is converted into a single value, and a final value is calculated, which is accumulated as an example
BigDecimal result = Lists.newArrayList(BigDecimal.valueOf(1), BigDecimal.valueOf(2)).stream().reduce(BigDecimal.ZERO, BigDecimal::add);
allMatch,anyMatch,noneMatch
// All elements are greater than 1, returning true boolean result1 = Lists.newArrayList(1, 2, 3, 4).stream().allMatch(item -> item > 1); // Any element greater than 1 returns true boolean result2 = Lists.newArrayList(1, 2, 3, 4).stream().anyMatch(item -> item > 1); // No element greater than 1, returns true boolean result3 = Lists.newArrayList(1, 2, 3, 4).stream().noneMatch(item -> item > 1);
count
Number value of Statistics
long result1 = Lists.newArrayList(1, 2, 3, 4).stream().count();
findAny,findFirst
If there is data, one is returned, the difference is that in parallel processing, findAny returns when it matches the data, and findFirst needs to wait for all data processing to complete to return the first item, so findAny is more efficient in parallel processing
// Get any one back in time Integer result1 = Lists.newArrayList(1, 2, 3, 4).stream().findAny().get(); // All elements are executed and returned to the first Integer result12= Lists.newArrayList(1, 2, 3, 4).parallelStream().findFirst().get();
forEach,forEachOrdered
Traversing through all elements, such as output operations, why do you need forEachOrdered with forEach? Especially in parallel execution, element execution is out of order, forEachOrdered can output results in order
// Output all elements Lists.newArrayList(1, 2, 3, 4).stream().forEach(System.out::println); // Sequential Output Lists.newArrayList(1, 2, 3, 4).parallelStream().forEachOrdered(System.out::println);
max,min
Gets the maximum and minimum values of the elements in the stream. The following examples show the maximum value, and the minimum value is the same
// Simple data type Integer result = Lists.newArrayList(1, 2, 3, 4).stream().max(Integer::compare).get(); // Compare attributes in the object to get the largest record Demo result = Lists.newArrayList(new Demo()).stream().max(comparing(Demo::getAge)).get();
4. Stream Notes
When using parallel streams for processing, the final data must be collected, otherwise data may be lost, such as using collect or reduce, which means using collect and reduce to use parallelStream, where the entire stream processing is thread-safe