Stream
Java8新特性增加了一个新的抽象称为流Stream。目的在于弥补Java函数式编程的缺陷。将要处理的元素集合看作一种流,流在管道中传输,并且可以在管道的节点上进行处理,比如筛选, 排序,聚合等。元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。
简单来说,Stream 就如同一个迭代器(Iterator),单向且数据只能遍历一次。
流的用法
在使用一个流时,一般是:获取一个数据源(source)-> 数据转换 -> 执行操作获取想要的结果。
其中数据转换每次都返回一个新的Stream,这样多个操作就构成了一个管道。最后从管道中获取我们的结果。
流的创建
实际使用时,有多种方式生成Stream。
方式 | 方法 | 例子 |
Collection 和Arrays | Collection.stream() Collection.parallelStream() Arrays.stream(ary) | Stream<String> stream = strs.stream(); Stream<String> streams = strs.parallelStream(); Stream<String> stream = Arrays.stream(new String[]{"First", "Second"}); |
BufferedReader | java.io.BufferedReader.lines() | BufferedReader br = new BufferedReader(new FileReader("text.txt")); Stream<String> stream = br.lines(); |
静态工厂 | java.util.stream.IntStream.range() java.nio.file.Files.walk() | |
自己构建 | java.util.Spliterator | |
其它 | Random.ints() BitSet.stream() Pattern.splitAsStream(java.lang.CharSequence) JarFile.stream() |
流的操作类型
流的操作类型有两种:
Intermediate:一个流可以后面跟随零个或多个 intermediate 操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。
Terminal:一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。Terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个 side effect。
流的使用
基本类型
对于基本数值型,目前有三种对应的包装类型 Stream:IntStream、LongStream、DoubleStream。当然我们也可以用 Stream<Integer>、Stream<Long> >、Stream<Double>,我们知道Java中 拆箱和装箱的操作会很耗时,所以特别为这三种基本数值型提供了对应的 Stream。操作
上边说了操作分两类,下边分两类说明:
操作类型 | 函数 | 作用 |
Intermediate | map | 映射成 output Stream 的另外一个元素 |
flatMap | 是一对多映射关系的,这时需要 flatMap | |
mapToInt | ||
filter | ||
distinct | ||
sorted | ||
peek | 对每个元素执行操作并返回一个新的 Stream | |
limit | 返回 Stream 的前面 n 个元素 | |
skip | 扔掉前 n 个元素 | |
parallel | ||
sequential | ||
unordered | ||
Terminal | forEach | forEach 方法接收一个 Lambda 表达式,然后在 Stream 的每一个元素上执行该表达式 |
forEachOrdered | ||
toArray | ||
reduce | 给一个起始值,把 Stream 元素组合起来, | |
collect | ||
min | ||
max | ||
count | ||
anyMatch | ||
allMatch | ||
noneMatch | ||
findFirst | ||
findAny | ||
iterator |
lambda表达式
Java 8终于引进了lambda表达式,java离函数式编程更近一步。学过ES6 的该知道,lambda表达式又叫箭头函数。在Java8之前,一般排序时代码如下:
List<String> strs = new ArrayList<String>();
Collections.sort(strs, new Comparator<String>() {
public int compare(String str1, String str2) {
return str1.compareToIgnoreCase(str2);
}
});
我们知道,实际上这里生成了一个匿名内部类。而在Java8中,可以写成如下:
Collections.sort(strs, (str1, str2) -> {
return str1.compareToIgnoreCase(str2);
});
符号 ()->{} 表示lambda函数。然后根据返回类型,自动推导出函数返回类型。
那么Lambda表达式是怎么实现的呢?下边是一个新建的类。
public class TryLambda {
public static void main(String[] args) {
List<String> strs = new ArrayList<>();
strs.add("kaka");
strs.add("hust");
Collections.sort(strs, (str1, str2)-> {
return str1.compareTo(str2);
});
}
}
然后对其进行编译:
javac TryLambda.java
此时生成 TryLambda.class 文件。然后利用JDK自带工具,对该文件进行反编译:
javap -p TryLambda.class
发现输出如下:
Compiled from "TryLambda.java"
public class com.lambda.TryLambda {
public com.lambda.TryLambda();
public static void main(java.lang.String[]);
private static int lambda$main$0(java.lang.String, java.lang.String);
}
我们知道,从上边的结果中发,它根据lambda表达式生成了一个静态的私有函数。
好了,继续执行以下命令查看下它的字节码指令:
javap -c -p TryLambda.class
结果如下:
Compiled from "TryLambda.java"
public class com.lambda.TryLambda {
public com.lambda.TryLambda();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: ldc #4 // String kaka
11: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
16: pop
17: aload_1
18: ldc #6 // String hust
20: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
25: pop
26: aload_1
27: invokedynamic #7, 0 // InvokeDynamic #0:compare:()Ljava/util/Comparator;
32: invokestatic #8 // Method java/util/Collections.sort:(Ljava/util/List;Ljava/util/Comparator;)V
35: return
private static int lambda$main$0(java.lang.String, java.lang.String);
Code:
0: aload_0
1: aload_1
2: invokevirtual #9 // Method java/lang/String.compareTo:(Ljava/lang/String;)I
5: ireturn
}
待续