Java 8 Stream

Java 8 新特性Java 8 New Features


The Java 8 API added a new abstraction called Stream, which allows you to process data in a declarative way.

Stream provides a high-level abstraction for Java collection operations and expressions in an intuitive way similar to querying data from a database with SQL statements.

The Stream API can greatly improve the productivity of Java programmers, allowing them to write efficient, clean, and concise code.

This style treats the collection of elements to be processed as a stream. The stream is transmitted in a pipeline and can be processed at pipeline nodes, such as filtering, sorting, aggregation, etc.

The element stream passes through intermediate operations in the pipeline, and finally the previous processing results are obtained by a terminal operation.

+--------------------+       +------+   +------+   +---+   +-------+
| stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
+--------------------+       +------+   +------+   +---+   +-------+

The above process is converted into Java code as:

List<Integer> transactionsIds = 
widgets.stream()
             .filter(b -> b.getColor() == RED)
             .sorted((x,y) -> x.getWeight() - y.getWeight())
             .mapToInt(Widget::getWeight)
             .sum();

What is Stream?

Stream is a queue of elements from a data source and supports aggregate operations.

  • Elements are objects of a specific type, forming a queue. Stream in Java does not store elements; instead, it computes them on demand.
  • Data sourceThe source of the stream. It can be a collection, array, I/O channel, generator, etc.
  • Aggregate operationsOperations similar to SQL statements, such as filter, map, reduce, find, match, sorted, etc.

Unlike previous Collection operations, Stream operations have two basic characteristics:

  • PipeliningPipelining: Intermediate operations all return the stream object itself. This allows multiple operations to be chained into a pipeline, just like a fluent style. This allows optimization of operations, such as laziness and short-circuiting.
  • Internal iterationInternal iteration: Previously, collection traversal was done via Iterator or For-Each, explicitly iterating outside the collection, which is called external iteration. Stream provides an internal iteration method, implemented through the Visitor pattern.

Generating Streams

In Java 8, the collection interface has two methods to generate streams:

  • stream()− Creates a serial stream for the collection.

  • parallelStream()− Creates a parallel stream for the collection.

List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());

forEach

Stream provides a new method 'forEach' to iterate each datum in the stream. The following code snippet uses forEach to output 10 random numbers:

Random random = new Random(); random.ints().limit(10).forEach(System.out::println);

map

The map method is used to map each element to its corresponding result. The following code snippet uses map to output the squares of the elements:

List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); //Get the corresponding square numbers List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());

filter

The filter method is used to filter out elements based on a set condition. The following code snippet uses the filter method to filter out empty strings:

List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); //Get the count of empty strings long count = strings.stream().filter(string -> string.isEmpty()).count();

limit

The limit method is used to get a stream with a specified quantity. The following code snippet uses the limit method to print 10 data items:

Random random = new Random(); random.ints().limit(10).forEach(System.out::println);

sorted

The sorted method is used to sort the stream. The following code snippet uses the sorted method to sort the 10 output random numbers:

Random random = new Random(); random.ints().limit(10).sorted().forEach(System.out::println);

Parallel Program

parallelStream is an alternative method for stream parallel processing. In the following example, we use parallelStream to output the count of empty strings:

List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); //Get the count of empty strings long count = strings.parallelStream().filter(string -> string.isEmpty()).count();

We can easily switch between sequential running and parallel.


Collectors

The Collectors class implements many reduction operations, such as converting streams into collections and aggregating elements. Collectors can be used to return lists or strings:

List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList()); System.out.println("Filtered list:" + filtered); String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", ")); System.out.println("Merged strings:" + mergedString);

Statistics

In addition, some collectors that produce statistical results are also very useful. They are mainly used on primitive types such as int, double, and long, and can be used to produce statistical results like the following.

List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics(); System.out.println("The largest number in the list:" + stats.getMax()); System.out.println("The smallest number in the list:" + stats.getMin()); System.out.println("Sum of all numbers:" + stats.getSum()); System.out.println("Average:" + stats.getAverage());

Stream Complete Example

Put the following code into the Java8Tester.java file:

Java8Tester.java File

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("Using Java 7:"); //Count empty strings List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); System.out.println("List:" +strings); long count = getCountEmptyStringUsingJava7(strings); System.out.println("Number of empty strings:" + count); count = getCountLength3UsingJava7(strings); System.out.println("Number of strings with length 3:" + count); //Remove empty strings List<String> filtered = deleteEmptyStringsUsingJava7(strings); System.out.println("Filtered list:" + filtered); //Remove empty strings and merge them with commas String mergedString = getMergedStringUsingJava7(strings,", "); System.out.println("Merged strings:" + mergedString); List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); //Get the squares of the list elements List<Integer> squaresList = getSquares(numbers); System.out.println("List of square numbers:" + squaresList); List<Integer> integers = Arrays.asList(1,2,13,4,15,6,17,8,19); System.out.println("List:" +integers); System.out.println("The largest number in the list:" + getMax(integers)); System.out.println("The smallest 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 numbers:"); //Output 10 random numbers Random random = new Random(); for(int i=0; i < 10; i++){ System.out.println(random.nextInt()); } System.out.println("Using Java 8:"); System.out.println("List:" +strings); count = strings.stream().filter(string->string.isEmpty()).count(); System.out.println("Number of empty strings:" + count); count = strings.stream().filter(string -> string.length() == 3).count(); System.out.println("Number of strings with length 3:" + 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("Merged strings:" + 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("The largest number in the list:" + stats.getMax()); System.out.println("The smallest 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 numbers:"); random.ints().limit(10).sorted().forEach(System.out::println); //Parallel processing count = strings.parallelStream().filter(string -> string.isEmpty()).count(); System.out.println("Number of empty strings:" + 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(); } }

Executing the above script produces the following output:

$ javac Java8Tester.java 
$ java Java8Tester
使用 Java 7: 
列表: [abc, , bc, efg, abcd, , jkl]
空字符数量为: 2
字符串长度为 3 的数量为: 3
筛选后的列表: [abc, bc, efg, abcd, jkl]
合并字符串: abc, bc, efg, abcd, jkl
平方数列表: [9, 4, 49, 25]
列表: [1, 2, 13, 4, 15, 6, 17, 8, 19]
列表中最大的数 : 19
列表中最小的数 : 1
所有数之和 : 85
平均数 : 9
随机数: 
-393170844
-963842252
447036679
-1043163142
-881079698
221586850
-1101570113
576190039
-1045184578
1647841045
使用 Java 8: 
列表: [abc, , bc, efg, abcd, , jkl]
空字符串数量为: 2
字符串长度为 3 的数量为: 3
筛选后的列表: [abc, bc, efg, abcd, jkl]
合并字符串: abc, bc, efg, abcd, jkl
Squares List: [9, 4, 49, 25]
列表: [1, 2, 13, 4, 15, 6, 17, 8, 19]
列表中最大的数 : 19
列表中最小的数 : 1
所有数之和 : 85
平均数 : 9.444444444444445
随机数: 
-1743813696
-1301974944
-1299484995
-779981186
136544902
555792023
1243315896
1264920849
1472077135
1706423674
空字符串的数量为: 2

Java 8 新特性Java 8 New Features

Other extensions