当你处理集合时,通常会迭代所有元素并对其中的每一个进行处理。例如,假设我们希望统计一个文件中的所有长单词:
package java8test;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
public class T5 {
public static void main(String[] args) {
try {
String contents = new String(Files.readAllBytes(
Paths.get("/home/fuhd/work/workspace/javaee/wwos.platform/pom.xml")),
StandardCharsets.UTF_8);
List<String> words = Arrays.asList(contents.split("\n"));
//进行迭代
int count = 0;
for(String w: words){
if(w.length() > 12) count++;
}
System.out.println(count);
} catch (IOException e) {
e.printStackTrace();
}
}
}
这里面有什么错误吗?其实没有——只是它很难被并行计算。这也是java8引入大量操作符的原因。在Java8中,实现相同功能的操作符如下所示:
package java8test;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
public class T5 {
public static void main(String[] args) {
try {
String contents = new String(Files.readAllBytes(
Paths.get("/home/fuhd/work/workspace/javaee/wwos.platform/pom.xml")),
StandardCharsets.UTF_8);
List<String> words = Arrays.asList(contents.split("\n"));
//注意这一句
long count = words.stream().filter(w -> w.length() > 12).count();
System.out.println(count);
} catch (IOException e) {
e.printStackTrace();
}
}
}
stream方法会为单词列表生成一个Stream。filter方法会返回另一个只包含单词长度大于12的Stream。count方法会将Stream化简为一个结果。
一个Stream表面上看与一个集合很类似,允许你改变和获取数据。但是实际上它与集合是有很大区别的:
Stream自己不会存储元素。元素可能被存储在底层的集合中,或者根据需要产生出来。
Stream操作符不会改变源对象。相反,它们会返回一个持有结果的新Stream。
Stream操作符可能是延迟执行的。这意味着它们会等到需要结果的时候才执行。
许多人发现Stream表达式比循环的可读性更好。此外,它们还很容易进行并行执行。以下是一段如何并行统计长单词的代码:
package java8test;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
public class T5 {
public static void main(String[] args) {
try {
String contents = new String(Files.readAllBytes(
Paths.get("/home/fuhd/work/workspace/javaee/wwos.platform/pom.xml")),
StandardCharsets.UTF_8);
List<String> words = Arrays.asList(contents.split("\n"));
//注意这一句,stream()改成了parallelStream()方法
long count = words.parallelStream().filter(w -> w.length() > 12).count();
System.out.println(count);
} catch (IOException e) {
e.printStackTrace();
}
}
}
只要将stream()方法改成parallelStream方法,就可以让Stream API并行执行过滤和统计操作。
Stream遵循“做什么,而不是怎么去做”的原则。在我们的示例中,描述了需要做什么:获得长单词并对它们的个数进行统计。我们没有指定按照什么顺序,或者在哪个线程中做,它们都是理所应当发生的。相反,循环在一开始就需要指定如何进行计算,因此就失去了优化的机会。
当你使用Stream时,你会通过三个阶段来建立一个操作流水线:
创建一个Stream。
在一个或多个步骤中,指定将初始Stream转换为另一个Stream的中间操作。
使用一个终止操作来产生一个结果。该操作会强制它之前的延迟操作立即执行。在这之后,该Stream就不会再被使用
在我们的示例中,通过stream或者parallelStream方法来创建Stream,再通过filter方法对其进行转换,而count就是终止操作
注意:Stream操作不会按照元素的调用顺序执行。在我们的例子中,只有在count被调用的时候才会执行Stream操作。当count方法需要第一个元素时,filter方法会开始请求各个元素,直到找到一个长度大于12的元素。