preface
Lambda expression is the basis of stream. Beginners are recommended to learn lambda expression - > first, Lambda expression of new features in Java 8
1, Get to know Stream
1.1.Stream overview
Stream is about algorithm and computation, which allows you to process data sets declaratively. It can be regarded as a high-level iterator to traverse data sets.
In addition, unlike iterators, streams can be processed in parallel. The data can be divided into multiple segments, each of which is executed in different threads, and finally the results are output together.
When we use a stream, there are usually three basic steps:
Obtain a data source → data conversion → execute the operation to obtain the desired result. Each time the original Stream object is converted, a new Stream object is returned (there can be multiple conversions), which allows the operation to be arranged like a chain and become a pipe, as shown in the following figure.
1.2. Three characteristics of flow
1. The stream does not store elements;
2. The stream will not modify its data source;
3. Flow execution has delay characteristics;
2, Creation of Sream flow
2.1. From an array or collection
1 .Arrays.stream(T array) or Stream.of()
2 .Collection.stream()
3 .Collection.parallelStream()
@Test public void testArrayStream() { //1. Through arrays stream //1.1 basic types Integer[] arr = new Integer[]{1,2,3,4,5,6,7}; //Via arrays stream Stream stream1 = Arrays.stream(arr); stream1.forEach(System.out::print); //2. Through stream of Stream<Integer> stream2 = Stream.of(1,2,3,4,5,6,7); stream2.forEach(System.out::print); } @Test public void testCollectionStream(){ List<Integer> strs = Arrays.asList(1,2,3,4,5,6,7); //Create normal flow Stream<Integer> stream = strs.stream(); //Create parallel stream Stream<Integer> stream1 = strs.parallelStream(); }
2.2. Create infinite flow
@Test public void test4(){ // iteration // public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) //Traverse the first 10 even numbers Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::print); System.out.println(); Stream.iterate(1,t->t+2).limit(5).forEach(System.out::print); System.out.println(); // generate // public static<T> Stream<T> generate(Supplier<T> s) Stream.generate(Math::random).limit(10).forEach(System.out::println); List<Double> collect = Stream.generate(Math::random).limit(10).collect(Collectors.toList()); Stream.generate(Math::random).limit(10).collect(Collectors.toList()); }
2.3. Create regular flow
@Test public void testUnlimitStream1(){ Stream.iterate(0,x -> x+1).limit(10).forEach(System.out::println); Stream.iterate(0,x -> x).limit(10).forEach(System.out::println); }
2.4. of() through Stream
@Test public void test3(){ Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6); }
3, Detailed explanation of Stream operation and examples
Stream operations are divided into intermediate operations and terminal operations. Intermediate operations will return another stream and can continue to perform the next intermediate operation.
The terminal operation is the return result. The terminal operation triggers the flow to perform intermediate operations. The terminal operation ends the life of this flow.
3.1. Intermediate operation
3.1.1.filter
The filter filters the original Stream, and the qualified original number is left to generate a new Stream.
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); // Gets the number of empty strings long count = strings.stream().filter(string -> string.isEmpty()).count();
3.1.2.sorted
sorted sorts the elements in the original Stream and generates a new Stream after sorting.
Random random = new Random(); random.ints().limit(10).sorted().forEach(System.out::println);
3.1.3.distinct
Remove duplicate values
@Test public void testDistinct() { Integer[] sixNums = {1, 2, 4, 3, 5, 5, 6}; Integer[] distinctedNums = Stream.of(sixNums).distinct().toArray(Integer[]::new); System.out.println(distinctedNums); }
3.1.4.map
map converts an existing stream to a new stream. map takes a function as a parameter, which acts on each element in the stream and maps it to a new element.
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); // Get the corresponding square number List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
3.1.5.flatMap
Flatten the flow, and each element in the flow is disassembled into a new flow. To use flatMap, you need to know the element type in the original stream in advance.
@Test public void testFlapMap1() { String[] words = {"Hello", "World"}; Stream.of(words) .map(word -> word.split("")) .flatMap(Arrays::stream).forEach(System.out::println); }
3.1.6.limit
Truncate the stream, which returns a stream no longer than the given length.
Random random = new Random(); random.ints().limit(10).forEach(System.out::println);
3.2. Terminal operation
Traverse each element in the flow. When you need to optimize a multi-core system, you can parallelstream() Foreach(), but the order of the original elements cannot be guaranteed. In parallel, the behavior of serial operation will be changed.
@Test public void testFlapMap1() { String[] words = {"Hello", "World"}; Stream.of(words) .map(word -> word.split("")) .flatMap(Arrays::stream).forEach(System.out::println); }
3.2.1.findFirst, findAny
//The functions of the two are similar. findAny does not require order, and the use of parallel flow has advantages. @Test public void testFindFirser() { String[] arr = new String[]{"yes", "YES", "no", "NO"}; Arrays.stream(arr).filter(str -> Objects.equals(str, "yes")).findFirst().ifPresent(System.out::println); }
3.2.2.allMatch, noneMatch, anyMatch
Stream provides three match methods. allMatch requires that all elements in the stream meet the conditions before returning true. Any match returns true as long as one element meets the conditions. Nonematch, on the contrary, returns true if all elements do not meet the conditions
@Test public void testMatch() { String[] arr = new String[]{"yes", "YES", "no", "NO"}; System.out.println(Arrays.stream(arr).noneMatch( str -> str.length() > 2)); System.out.println(Arrays.stream(arr).anyMatch( str -> str.length() > 2)); System.out.println(Arrays.stream(arr).allMatch( str -> str.length() > 2)); }
3.2.3.reduce
The main function of this method is to combine the Stream elements. It provides a starting value (seed), and then combines it with the first, second and Nth elements of the previous Stream according to the operation rules (BinaryOperator). In this sense, string splicing, sum, min, max and average of numerical values are special reduce. For example, the sum of Stream is equivalent to
Integer sum = integers.reduce(0, (a, b) -> a+b);
or
Integer sum = integers.reduce(0, Integer::sum);
@Test public void testReduce() { // String connection, concat = "ABCD" String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat); // Find the minimum value, minValue = -3.0 double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min); // Sum, sumValue = 10, with starting value int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum); // Summation, sumValue = 10, no starting value sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get(); // Filter, string connection, concat = "ace" concat = Stream.of("a", "B", "c", "D", "e", "F").filter(x -> x.compareTo("Z") > 0).reduce("", String::concat); }
3.2.4. Statistics (count/averaging)
Reference article here: Java8 Stream: 20 instances of 20000 words, playing with the filtering, reduction, grouping and aggregation of collections
Collectors provide a series of static methods for data statistics:
Count: count
Average value: averagingInt, averagingLong, averagingDouble
Max value: maxBy, minBy
Summation: summerint, summerlong, summerdouble
Statistics of all the above: summarizingInt, summarizingLong, summarizingDouble
public class StreamTest { public static void main(String[] args) { List<Person> personList = new ArrayList<Person>(); personList.add(new Person("Tom", 8900, 23, "male", "New York")); personList.add(new Person("Jack", 7000, 25, "male", "Washington")); personList.add(new Person("Lily", 7800, 21, "female", "Washington")); // Total Long count = personList.stream().collect(Collectors.counting()); // Average wage Double average = personList.stream().collect(Collectors.averagingDouble(Person::getSalary)); // Seek maximum wage Optional<Integer> max = personList.stream().map(Person::getSalary).collect(Collectors.maxBy(Integer::compare)); // Sum of wages Integer sum = personList.stream().collect(Collectors.summingInt(Person::getSalary)); // One time statistics of all information DoubleSummaryStatistics collect = personList.stream().collect(Collectors.summarizingDouble(Person::getSalary)); System.out.println("Total number of employees:" + count); System.out.println("Average salary of employees:" + average); System.out.println("Total employee salary:" + sum); System.out.println("Employee salary statistics:" + collect); } }
Operation results:
Total number of employees: 3 Average salary of employees: 7900.0 Total employee salary: 23700 Employee salary statistics: DoubleSummaryStatistics{count=3, sum=23700.000000,min=7000.000000, average=7900.000000, max=8900.000000}
3.2.5.collect
This is widely used in actual projects, and the results are generated through collect. It can generate various forms, such as array, list, set and map. In addition, it can be grouped.
public class Student { private String name; private Integer score; //-----getters and setters----- Student(String name, Integer score) { this.name = name; this.score = score; } public String getName() { return name; } public Integer getScore() { return score; } public String toString() { return "name: " + name + " score: " + score; } } Student[] students; @Before public void init(){ students = new Student[10]; for (int i = 0; i <= 3; i++){ Student student = new Student("user", i); students[i] = student; } for (int i = 3; i <= 6; i++){ Student student = new Student("user" + i, i + 1); students[i] = student; } for (int i = 6; i < 10; i++){ Student student = new Student("user" + i, i + 2); students[i] = student; } } @Test public void testCollect1(){ /** * Generate List */ List<Student> list = Arrays.stream(students).collect(Collectors.toList()); list.forEach((x) -> System.out.println(x)); /** * Generate Set */ Set<Student> set = Arrays.stream(students).collect(Collectors.toSet()); set.forEach((x)-> System.out.println(x)); /** * If the Map contains the same key, the third parameter needs to be provided. Otherwise, an error is reported. Here, the old value is overwritten */ Map<String,Integer> map = Arrays.stream(students).collect(Collectors.toMap(Student::getName, Student::getScore, (v1, v2) -> v2)); map.forEach((x, y) -> System.out.println(x + "->" + y)); } /** * Generate array */ @Test public void testCollect2(){ Student[] s = Arrays.stream(students).toArray(Student[]::new); for (int i=0; i< s.length; i++) System.out.println(s[i]); }
3.2.6.groupingBy
groupingBy can group and fragment streams according to certain conditions. The condition is key. The elements after grouping form a new stream. Intermediate and terminal operations can be performed on the new stream. A List is generated by default. Finally, the type of Map generated. Key is the grouping condition, and value depends on the type generated after operating the grouped flow. There will be a problem of duplicate keys when generating a Map. Using groupingBy to put the value value of duplicate keys into a List can also solve this problem.
If you want to group according to multiple conditions, one method is groupingB nesting, using groupBy to group multiple times; Another method is to splice multiple grouping conditions into a key and group according to the spliced key.
@Test public void testGroupBy1(){ Map<String,List<Student>> map = Arrays.stream(students).collect(Collectors.groupingBy(Student::getName)); map.forEach((x,y)-> System.out.println(x+"->"+y)); } /** * If there are only two types, using partitioningBy is more efficient than grouping by */ @Test public void testPartitioningBy(){ Map<Boolean,List<Student>> map = Arrays.stream(students).collect(Collectors.partitioningBy(x -> x.getScore() > 5)); map.forEach((x, y)-> System.out.println(x+ "->" + y)); } /** * downstream Specify type */ @Test public void testGroupBy2(){ Map<String,Set<Student>> map = Arrays.stream(students).collect(Collectors.groupingBy(Student::getName, Collectors.toSet())); map.forEach((x, y)-> System.out.println(x + "->"+ y)); } /** * downstream Aggregation operation */ @Test public void testGroupBy3(){ /** * counting */ Map<String,Long> map1 = Arrays.stream(students).collect(Collectors.groupingBy(Student::getName, Collectors.counting())); map1.forEach((x, y)-> System.out.println(x + "->" + y)); /** * summingInt */ Map<String,Integer> map2 = Arrays.stream(students).collect(Collectors.groupingBy(Student::getName, Collectors.summingInt(Student::getScore))); map2.forEach((x,y) -> System.out.println(x + "->" + y)); /** * maxBy */ Map<String,Optional<Student>> map3 = Arrays.stream(students).collect(Collectors.groupingBy(Student::getName, Collectors.maxBy(Comparator.comparing(Student::getScore)))); map3.forEach((x, y)-> System.out.println(x + "->" + y)); /** * mapping, This is also used more. You can use the element variable as value or create a new object as value */ Map<String,Set<Integer>> map4 = Arrays.stream(students).collect(Collectors.groupingBy(Student::getName, Collectors.mapping(Student::getScore, Collectors.toSet()))); map4.forEach((x, y)-> System.out.println(x + "->" + y)); /** * mapping, Use elements to group multiple variables */ Map<String, Map<Integer, List<Student>>> map5 = Arrays.stream(students).collect(Collectors.groupingBy(Student::getName, Collectors.groupingBy(Student::getScore))); map5.forEach((x, y)-> System.out.println(x + "->" + y)); /** * mapping, The second method is to splice multiple variables. I recommend this method */ Map<String, List<Student>> map6 = Arrays.stream(students).collect(Collectors.groupingBy(student -> student.getName() + "_" + student.getScore())); map6.forEach((x, y)-> System.out.println(x + "->" + y)); }
3.3. Collectors operation in stream
3.3.1.Collectors.toList()
List<String> listResult = list.stream().collect(Collectors.toList()); log.info("{}",listResult);
Convert stream to list. The list converted here is ArrayList. If you want to convert it to a specific list, you need to use the toCollection method.
3.3.2.Collectors.toSet()
Set<String> setResult = list.stream().collect(Collectors.toSet()); log.info("{}",setResult);
toSet converts a Stream to a set. HashSet is converted here. If you need to specify a set specifically, you need to use the toCollection method.
Because there are no duplicate elements in set, if we use duplicateList to convert, we will find that there is only one jack in the final result.
Set<String> duplicateSetResult = duplicateList.stream().collect(Collectors.toSet()); log.info("{}",duplicateSetResult);
3.3.3.Collectors.toCollection()
The togap and toset above are all of specific types. If we need to customize them, we can use toCollection()
List<String> custListResult = list.stream().collect(Collectors.toCollection(LinkedList::new)); log.info("{}",custListResult);
In the above example, we convert it to LinkedList.
3.3.4.Collectors.toMap()
toMap receives two parameters. The first parameter is keyMapper and the second parameter is valueMapper:
Map<String, Integer> mapResult = list.stream() .collect(Collectors.toMap(Function.identity(), String::length)); log.info("{}",mapResult);
If there are duplicate values in the stream, the conversion will report IllegalStateException:
Map<String, Integer> duplicateMapResult = duplicateList.stream() .collect(Collectors.toMap(Function.identity(), String::length));
How to solve this problem? We can do this:
Map<String, Integer> duplicateMapResult2 = duplicateList.stream() .collect(Collectors.toMap(Function.identity(), String::length, (item, identicalItem) -> item)); log.info("{}",duplicateMapResult2);
Add the third parameter mergeFunction in the toMap to solve the conflict.
3.3.5.Collectors.collectingAndThen()
collectingAndThen allows us to do another operation on the generated collection.
List<String> collectAndThenResult = list.stream() .collect(Collectors.collectingAndThen(Collectors.toList(), l -> {return new ArrayList<>(l);})); log.info("{}",collectAndThenResult);
3.3.6.Collectors.joining()
Joining is used to connect elements in the stream:
String joinResult = list.stream().collect(Collectors.joining()); log.info("{}",joinResult); String joinResult1 = list.stream().collect(Collectors.joining(" ")); log.info("{}",joinResult1); String joinResult2 = list.stream().collect(Collectors.joining(" ", "prefix","suffix")); log.info("{}",joinResult2);
You can take no parameters, one parameter or three parameters, which can be selected according to our needs.
3.3.7.Collectors.counting()
counting is mainly used to count the number of elements in the stream:
Long countResult = list.stream().collect(Collectors.counting()); log.info("{}",countResult);
3.3.8.Collectors.summarizingDouble/Long/Int()
SummarizingDouble/Long/Int generates statistics for the elements in the stream. The returned result is a statistics class:
IntSummaryStatistics intResult = list.stream() .collect(Collectors.summarizingInt(String::length)); log.info("{}",intResult);
Output results:
22:22:35.238 [main] INFO com.flydean.CollectorUsage - IntSummaryStatistics{count=4, sum=16, min=3, average=4.000000, max=5}
3.3.9.Collectors.averagingDouble/Long/Int()
Averagedouble / long / int() averages the elements in the stream:
Double averageResult = list.stream().collect(Collectors.averagingInt(String::length)); log.info("{}",averageResult);
3.3.10.Collectors.summingDouble/Long/Int()
summingDouble/Long/Int() performs a sum operation on the elements in the stream:
Double summingResult = list.stream().collect(Collectors.summingDouble(String::length)); log.info("{}",summingResult);
3.3.11.Collectors.maxBy()/minBy()
maxBy()/minBy() returns the maximum or minimum value in the stream according to the provided Comparator:
Optional<String> maxByResult = list.stream().collect(Collectors.maxBy(Comparator.naturalOrder())); log.info("{}",maxByResult);
3.3.12.Collectors.groupingBy()
GroupingBy groups according to some properties and returns a Map:
Map<Integer, Set<String>> groupByResult = list.stream() .collect(Collectors.groupingBy(String::length, Collectors.toSet())); log.info("{}",groupByResult);
3.3.13.Collectors.partitioningBy()
PartitioningBy is a special groupingBy. PartitioningBy returns a Map. This Map takes the boolean value as the key, which divides the stream into two parts. One part matches the partitionby condition, and the other part does not meet the condition:
Map<Boolean, List<String>> partitionResult = list.stream() .collect(Collectors.partitioningBy(s -> s.length() > 3)); log.info("{}",partitionResult);
See the operation results:
22:39:37.082 [main] INFO com.flydean.CollectorUsage - {false=[bob], true=[jack, alice, mark]}
The results are divided into two parts.
3.4.Stream full instance
import java.util.ArrayList; import java.util.Arrays; import java.util.IntSummaryStatistics; import java.util.List; import java.util.Random; import java.util.stream.Collectors; import java.util.Map; public class Java8Tester { public static void main(String args[]){ System.out.println("use Java 7: "); // Calculate empty string List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); System.out.println("list: " +strings); long count = getCountEmptyStringUsingJava7(strings); System.out.println("The number of empty characters is: " + count); count = getCountLength3UsingJava7(strings); System.out.println("The number of strings with length 3 is: " + count); // Delete empty string List<String> filtered = deleteEmptyStringsUsingJava7(strings); System.out.println("Filtered list: " + filtered); // Delete empty strings and combine them with commas String mergedString = getMergedStringUsingJava7(strings,", "); System.out.println("Merge string: " + mergedString); List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); // Gets the square number of list elements List<Integer> squaresList = getSquares(numbers); System.out.println("Square list: " + squaresList); List<Integer> integers = Arrays.asList(1,2,13,4,15,6,17,8,19); System.out.println("list: " +integers); System.out.println("Maximum number of in the list : " + getMax(integers)); System.out.println("Minimum number in the list : " + getMin(integers)); System.out.println("Sum of all numbers : " + getSum(integers)); System.out.println("average : " + getAverage(integers)); System.out.println("random number: "); // Output 10 random numbers Random random = new Random(); for(int i=0; i < 10; i++){ System.out.println(random.nextInt()); } System.out.println("use Java 8: "); System.out.println("list: " +strings); count = strings.stream().filter(string->string.isEmpty()).count(); System.out.println("The number of empty strings is: " + count); count = strings.stream().filter(string -> string.length() == 3).count(); System.out.println("The number of strings with length 3 is: " + count); filtered = strings.stream().filter(string ->!string.isEmpty()).collect(Collectors.toList()); System.out.println("Filtered list: " + filtered); mergedString = strings.stream().filter(string ->!string.isEmpty()).collect(Collectors.joining(", ")); System.out.println("Merge string: " + mergedString); squaresList = numbers.stream().map( i ->i*i).distinct().collect(Collectors.toList()); System.out.println("Squares List: " + squaresList); System.out.println("list: " +integers); IntSummaryStatistics stats = integers.stream().mapToInt((x) ->x).summaryStatistics(); System.out.println("Maximum number of in the list : " + stats.getMax()); System.out.println("Minimum number in the list : " + stats.getMin()); System.out.println("Sum of all numbers : " + stats.getSum()); System.out.println("average : " + stats.getAverage()); System.out.println("random number: "); random.ints().limit(10).sorted().forEach(System.out::println); // parallel processing count = strings.parallelStream().filter(string -> string.isEmpty()).count(); System.out.println("The number of empty strings is: " + count); } private static int getCountEmptyStringUsingJava7(List<String> strings){ int count = 0; for(String string: strings){ if(string.isEmpty()){ count++; } } return count; } private static int getCountLength3UsingJava7(List<String> strings){ int count = 0; for(String string: strings){ if(string.length() == 3){ count++; } } return count; } private static List<String> deleteEmptyStringsUsingJava7(List<String> strings){ List<String> filteredList = new ArrayList<String>(); for(String string: strings){ if(!string.isEmpty()){ filteredList.add(string); } } return filteredList; } private static String getMergedStringUsingJava7(List<String> strings, String separator){ StringBuilder stringBuilder = new StringBuilder(); for(String string: strings){ if(!string.isEmpty()){ stringBuilder.append(string); stringBuilder.append(separator); } } String mergedString = stringBuilder.toString(); return mergedString.substring(0, mergedString.length()-2); } private static List<Integer> getSquares(List<Integer> numbers){ List<Integer> squaresList = new ArrayList<Integer>(); for(Integer number: numbers){ Integer square = new Integer(number.intValue() * number.intValue()); if(!squaresList.contains(square)){ squaresList.add(square); } } return squaresList; } private static int getMax(List<Integer> numbers){ int max = numbers.get(0); for(int i=1;i < numbers.size();i++){ Integer number = numbers.get(i); if(number.intValue() > max){ max = number.intValue(); } } return max; } private static int getMin(List<Integer> numbers){ int min = numbers.get(0); for(int i=1;i < numbers.size();i++){ Integer number = numbers.get(i); if(number.intValue() < min){ min = number.intValue(); } } return min; } private static int getSum(List numbers){ int sum = (int)(numbers.get(0)); for(int i=1;i < numbers.size();i++){ sum += (int)numbers.get(i); } return sum; } private static int getAverage(List<Integer> numbers){ return getSum(numbers) / numbers.size(); } }
Execute the above script, and the output result is:
$ javac Java8Tester.java $ java Java8Tester use Java 7: list: [abc, , bc, efg, abcd, , jkl] The number of empty characters is: 2 The number of strings with length 3 is: 3 Filtered list: [abc, bc, efg, abcd, jkl] Merge string: abc, bc, efg, abcd, jkl Square list: [9, 4, 49, 25] list: [1, 2, 13, 4, 15, 6, 17, 8, 19] Maximum number of in the list : 19 Minimum number in the list : 1 Sum of all numbers : 85 average : 9 random number: -393170844 -963842252 447036679 -1043163142 -881079698 221586850 -1101570113 576190039 -1045184578 1647841045 use Java 8: list: [abc, , bc, efg, abcd, , jkl] The number of empty strings is: 2 The number of strings with length 3 is: 3 Filtered list: [abc, bc, efg, abcd, jkl] Merge string: abc, bc, efg, abcd, jkl Squares List: [9, 4, 49, 25] list: [1, 2, 13, 4, 15, 6, 17, 8, 19] Maximum number of in the list : 19 Minimum number in the list : 1 Sum of all numbers : 85 average : 9.444444444444445 random number: -1743813696 -1301974944 -1299484995 -779981186 136544902 555792023 1243315896 1264920849 1472077135 1706423674 The number of empty strings is: 2