Java 简明教程

Java - Streams

Stream 是 Java 8 中引入的新抽象层。使用流,你可以使用类似于 SQL 语句那样声明式的方式处理数据。例如,考虑以下 SQL 语句。

Stream is a new abstract layer introduced in Java 8. Using stream, you can process data in a declarative way similar to SQL statements. For example, consider the following SQL statement.

SELECT max(salary), employee_id, employee_name FROM Employee

上述 SQL 表达式自动返回薪水最高的员工的详细信息,而无需开发人员进行任何计算。使用 Java 中的集合框架,开发人员必须使用循环并进行重复检查。另一个问题是效率;由于多核处理器很容易使用,Java 开发人员必须编写可能很容易出错的并行代码处理。

The above SQL expression automatically returns the maximum salaried employee’s details, without doing any computation on the developer’s end. Using collections framework in Java, a developer has to use loops and make repeated checks. Another concern is efficiency; as multi-core processors are available at ease, a Java developer has to write parallel code processing that can be pretty error-prone.

为了解决此类问题,Java 8 引入了流的概念,使开发人员能够以声明方式处理数据,并利用多核架构,而无需针对它编写任何特定代码。

To resolve such issues, Java 8 introduced the concept of stream that lets the developer to process data declaratively and leverage multicore architecture without the need to write any specific code for it.

What is Stream in Java?

流表示来自某个源的对象序列,它支持聚合操作。以下是流的特性:

Stream represents a sequence of objects from a source, which supports aggregate operations. Following are the characteristics of a Stream −

  1. Sequence of elements − A stream provides a set of elements of specific type in a sequential manner. A stream gets/computes elements on demand. It never stores the elements.

  2. Source − Stream takes Collections, Arrays, or I/O resources as input source.

  3. Aggregate operations − Stream supports aggregate operations like filter, map, limit, reduce, find, match, and so on.

  4. Pipelining − Most of the stream operations return stream itself so that their result can be pipelined. These operations are called intermediate operations and their function is to take input, process them, and return output to the target. collect() method is a terminal operation which is normally present at the end of the pipelining operation to mark the end of the stream.

  5. Automatic iterations − Stream operations do the iterations internally over the source elements provided, in contrast to Collections where explicit iteration is required.

Generating Streams in Java

在 Java 8 中, Collection interface 有两种生成流的方法。

With Java 8, Collection interface has two methods to generate a Stream.

  1. stream() − Returns a sequential stream considering collection as its source.

  2. parallelStream() − Returns a parallel Stream considering collection as its source.

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

forEach Method

流提供了一个新的 'forEach' 方法来迭代流中的每个元素。以下代码片段展示了如何使用 forEach 打印 10 个随机数。

Stream has provided a new method 'forEach' to iterate each element of the stream. The following code segment shows how to print 10 random numbers using forEach.

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

map Method

'map' 方法用于将每个元素映射到它对应的结果。以下代码片段使用 map 打印数字的唯一平方。

The 'map' method is used to map each element to its corresponding result. The following code segment prints unique squares of numbers using map.

List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);

//get list of unique squares
List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());

filter Method

'filter' 方法用于基于某个条件排除元素。以下代码片段使用 filter 打印空字符串的计数。

The 'filter' method is used to eliminate elements based on a criteria. The following code segment prints a count of empty strings using filter.

List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");

//get count of empty string
int count = strings.stream().filter(string -> string.isEmpty()).count();

limit Method

'limit' 方法用于减小流的大小。以下代码片段展示了如何使用 limit 打印 10 个随机数。

The 'limit' method is used to reduce the size of the stream. The following code segment shows how to print 10 random numbers using limit.

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

sorted Method

'sorted' 方法用于对流进行排序。以下代码片段展示了如何以排序顺序打印 10 个随机数。

The 'sorted' method is used to sort the stream. The following code segment shows how to print 10 random numbers in a sorted order.

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

Parallel Processing

parallelStream 是用于并行处理的流的替代方案。看看以下使用 parallelStream 打印空字符串的计数的代码片段。

parallelStream is the alternative of stream for parallel processing. Take a look at the following code segment that prints a count of empty strings using parallelStream.

List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");

//get count of empty string
long count = strings.parallelStream().filter(string -> string.isEmpty()).count();

在顺序流和并行流之间切换非常容易。

It is very easy to switch between sequential and parallel streams.

Collectors

收集器用于组合对流元素进行处理的结果。收集器可用于返回列表或字符串。

Collectors are used to combine the result of processing on the elements of a stream. Collectors can be used to return a list or a string.

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 String: " + mergedString);

Statistics

从 Java 8 开始,已经引入了统计收集器,能够在执行流处理时计算所有统计信息。

With Java 8, statistics collectors are introduced to calculate all statistics when stream processing is being done.

List numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);

IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();

System.out.println("Highest number in List : " + stats.getMax());
System.out.println("Lowest number in List : " + stats.getMin());
System.out.println("Sum of all numbers : " + stats.getSum());
System.out.println("Average of all numbers : " + stats.getAverage());

Java Streams Example

在任意编辑器中创建以下 Java 程序,例如,在 C:\> JAVA 中创建。

Create the following Java program using any editor of your choice in, say, C:\> JAVA.

Java8Tester.java

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("Empty Strings: " + count);
      count = getCountLength3UsingJava7(strings);

      System.out.println("Strings of length 3: " + count);

      //Eliminate empty string
      List<String> filtered = deleteEmptyStringsUsingJava7(strings);
      System.out.println("Filtered List: " + filtered);

      //Eliminate empty string and join using comma.
      String mergedString = getMergedStringUsingJava7(strings,", ");
      System.out.println("Merged String: " + mergedString);
      List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);

      //get list of square of distinct numbers
      List<Integer> squaresList = getSquares(numbers);
      System.out.println("Squares List: " + squaresList);
      List<Integer> integers = Arrays.asList(1,2,13,4,15,6,17,8,19);

      System.out.println("List: " +integers);
      System.out.println("Highest number in List : " + getMax(integers));
      System.out.println("Lowest number in List : " + getMin(integers));
      System.out.println("Sum of all numbers : " + getSum(integers));
      System.out.println("Average of all numbers : " + getAverage(integers));
      System.out.println("Random Numbers: ");

      //print ten 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("Empty Strings: " + count);

      count = strings.stream().filter(string -> string.length() == 3).count();
      System.out.println("Strings of 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 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("Highest number in List : " + stats.getMax());
      System.out.println("Lowest number in List : " + stats.getMin());
      System.out.println("Sum of all numbers : " + stats.getSum());
      System.out.println("Average of all numbers : " + 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("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();
   }
}

Verify the Result

按照如下方式使用 javac 编译器编译类 −

Compile the class using javac compiler as follows −

C:\JAVA>javac Java8Tester.java

现在按照如下方式运行 Java8Tester −

Now run the Java8Tester as follows −

C:\JAVA>java Java8Tester

它应该产生以下结果 −

It should produce the following result −

Using Java 7:
List: [abc, , bc, efg, abcd, , jkl]
Empty Strings: 2
Strings of length 3: 3
Filtered List: [abc, bc, efg, abcd, jkl]
Merged String: abc, bc, efg, abcd, jkl
Squares List: [9, 4, 49, 25]
List: [1, 2, 13, 4, 15, 6, 17, 8, 19]
Highest number in List : 19
Lowest number in List : 1
Sum of all numbers : 85
Average of all numbers : 9
Random Numbers:
-1279735475
903418352
-1133928044
-1571118911
628530462
18407523
-881538250
-718932165
270259229
421676854
Using Java 8:
List: [abc, , bc, efg, abcd, , jkl]
Empty Strings: 2
Strings of length 3: 3
Filtered List: [abc, bc, efg, abcd, jkl]
Merged String: abc, bc, efg, abcd, jkl
Squares List: [9, 4, 49, 25]
List: [1, 2, 13, 4, 15, 6, 17, 8, 19]
Highest number in List : 19
Lowest number in List : 1
Sum of all numbers : 85
Average of all numbers : 9.444444444444445
Random Numbers:
-1009474951
-551240647
-2484714
181614550
933444268
1227850416
1579250773
1627454872
1683033687
1798939493
Empty Strings: 2