Java 8之Stream入门

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 stream = strs.stream();
Stream streams = strs.parallelStream();
Stream stream = Arrays.stream(new String[]{"First", "Second"});
BufferedReader java.io.BufferedReader.lines() BufferedReader br = new BufferedReader(new FileReader("text.txt"));
Stream 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、Stream >、Stream,我们知道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 strs = new ArrayList();
        Collections.sort(strs, new Comparator() {
            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 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."":()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."":()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
}

待续


你可能感兴趣的:(Java)