[Java1.8]_[Stream]

来源:互联网 发布:java源码阅读工具 编辑:程序博客网 时间:2024/05/16 12:41

Java 8 Stream教程

Stream不同于之前的I/O流,而是Java 8引入的函数式编程。

函数式编程

简单说,”函数式编程”是一种”编程范式”(programming paradigm),也就是如何编写程序的方法论。
它属于”结构化编程”的一种,主要思想是把运算过程尽量写成一系列嵌套的函数调用。

Stream 简单例子

用一个遍历数组的例子看看Stream的作用。

传统写法

List<String> myList =        Arrays.asList("a1", "a2", "b1", "c2", "c1");List<String> newList = new ArrayList<String>();//遍历for (String name : myList){    //筛选出字母开头是c的    if (name.startsWith("c"))    {        //转换成大写        String newName = name.toUpperCase();        newList.add(newName);    }}//排列Collections.sort(newList);//遍历输出newList.forEach(System.out::println);

Stream写法

List<String> myList =    Arrays.asList("a1", "a2", "b1", "c2", "c1");myList    .stream()                           //开始流操作    .filter(s -> s.startsWith("c"))     //筛选出开头字母是C的    .map(String::toUpperCase)           //转换成大写    .sorted()                           //排序    .forEach(System.out::println);      //遍历输出//C1//C2

Stream操作这里有用到Lambda表达式,大多数的Steam操作都可以用Lamdba表达式。Stream操作简化了很多,主要体现在逻辑清晰,没有过多的if/else判断语句。

Stream的种类

Stream分为sequential Stream(顺序流)和parallel stream(平行流),上面的例子是属于顺序流,通过stream()方法创建的流都是顺序流。

Stream

一般的集合如List和Set都支持Stream操作,但可以不通过创建集合来使用Stream操作,可以通过Stream.of()方法使用,代码如下:

Stream.of("a1", "a2", "a3")    .findFirst()    .ifPresent(System.out::println);  // a1

Primitive streams

Stream是处理对象的操作,primitive streams是处理处理传统类型的操作,如IntStream、LongStream 、DoubleStream分别处理对应的类型,这里展示InStream的简单使用:

IntStream.range(1, 4)    .forEach(System.out::println);// 1// 2// 3

Primitive streams和Streams的使用基本一样,只是有一些专门处理基本类型的方法,像上面的range(),还有如下例子:

Arrays.stream(new int[] {1, 2, 3})    .map(n -> 2 * n + 1)    .average()    .ifPresent(System.out::println);  // 5.0

average() 方法求平均值,Primitive streams和Stram都有方法讲对象和基本类型互相转换,代码如下:

Stream.of(1.0, 2.0, 3.0)    .mapToInt(Double::intValue)    .mapToObj(i -> "a" + i)    .forEach(System.out::println);// a1// a2// a3

Stream操作的顺序

先看下面例子代码:

Stream.of("d2", "a2", "b1", "b3", "c")    .filter(s -> {        System.out.println("filter: " + s);        return true;    });

该例子实际上是不会输出任何东西,因为Stream没有终端操作,只有中间操作。

再看看加入终端操作后的结果:

Stream.of("d2", "a2", "b1", "b3", "c")    .filter(s -> {        System.out.println("filter: " + s);        return true;    })    .forEach(s -> System.out.println("forEach: " + s));

输出结果:

filter:  d2forEach: d2filter:  a2forEach: a2filter:  b1forEach: b1filter:  b3forEach: b3filter:  cforEach: c

这里的输出结果可能会让你觉得很诧异,是数组元素按顺序执行完整个流操作,即要第一个元素执行完整个流操作才轮到第二个。

我们可以将元素重新排序然后再执行流操作,通过sort()方法,代码如下:

Stream.of("d2", "a2", "b1", "b3", "c")    .sorted((s1, s2) -> {        System.out.printf("sort: %s; %s\n", s1, s2);        return s1.compareTo(s2);    })    .filter(s -> {        System.out.println("filter: " + s);        return s.startsWith("a");    })    .map(s -> {        System.out.println("map: " + s);        return s.toUpperCase();    })    .forEach(s -> System.out.println("forEach: " + s));

输出结果:

sort:    a2; d2sort:    b1; a2sort:    b1; d2sort:    b1; a2sort:    b3; b1sort:    b3; d2sort:    c; b3sort:    c; d2filter:  a2map:     a2forEach: A2filter:  b1filter:  b3filter:  cfilter:  d2

Reusing Streams

Java 8的Stream不能被重复使用直到该Stream已经结束,其实Stream可以返回一个实例,看实例代码:

Stream<String> stream =    Stream.of("d2", "a2", "b1", "b3", "c")        .filter(s -> s.startsWith("a"));stream.anyMatch(s -> true);    // okstream.noneMatch(s -> true);   // exception

该结果会出现异常,因为stream的anyMatch()方法还没执行完就执行noneMatch(),异常如下:

java.lang.IllegalStateException: stream has already been operated upon or closed    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)    at java.util.stream.ReferencePipeline.noneMatch(ReferencePipeline.java:459)    at com.winterbe.java8.Streams5.test7(Streams5.java:38)    at com.winterbe.java8.Streams5.main(Streams5.java:28)

可以通过创建Supplier来实现重复执行,代码例子如下:

Supplier<Stream<String>> streamSupplier =    () -> Stream.of("d2", "a2", "b1", "b3", "c")            .filter(s -> s.startsWith("a"));streamSupplier.get().anyMatch(s -> true);   // okstreamSupplier.get().noneMatch(s -> true);  // ok

较为复杂的Stream操作

前面只是举例说明了一下,常用的map()、filter()、foreach()等操作很容易看明白,还有很多操作可以看这个链接
Java stream doc。

首先创建一个Person类方便说明,如下:

class Person {    String name;    int age;    Person(String name, int age) {        this.name = name;        this.age = age;    }    @Override    public String toString() {        return name;    }}List<Person> persons =    Arrays.asList(        new Person("Max", 18),        new Person("Peter", 23),        new Person("Pamela", 23),        new Person("David", 12));
Collect

Collect可以在最后将结果转化成一个集合返回,代码如下:

List<Person> filtered =    persons        .stream()        .filter(p -> p.name.startsWith("P"))        .collect(Collectors.toList());System.out.println(filtered);    // [Peter, Pamela]

可以看到还是比较简单实用的,通过Collectors.toList()方法直接返回一个List,如果需要set就用toSet(),
使用非常方便。

还可以生成Map指点类集合和求平均值,代码例子分别如下:

Map<Integer, String> map = persons    .stream()    .collect(Collectors.toMap(        p -> p.age,        p -> p.name,        (name1, name2) -> name1 + ";" + name2));System.out.println(map);
Double averageAge = persons    .stream()    .collect(Collectors.averagingInt(p -> p.age));System.out.println(averageAge);     // 19.0
FlatMap

如果集合或数组的元素本身也是集合或数组,而且我们就是要求访问集里面的集合的时候就要用到FlatMap,代码例子如下:

class Foo {    String name;    List<Bar> bars = new ArrayList<>();    Foo(String name) {        this.name = name;    }}class Bar {    String name;    Bar(String name) {        this.name = name;    }}
foos.stream()    .flatMap(f -> f.bars.stream())    .forEach(b -> System.out.println(b.name));// Bar1 <- Foo1// Bar2 <- Foo1// Bar3 <- Foo1// Bar1 <- Foo2// Bar2 <- Foo2// Bar3 <- Foo2// Bar1 <- Foo3// Bar2 <- Foo3// Bar3 <- Foo3
Reduce

Reduce的作用就是将流操作的多个结果汇集成一个,例子如下:

Person result =    persons        .stream()        .reduce(new Person("", 0), (p1, p2) -> {            p1.age += p2.age;            p1.name += p2.name;            return p1;        });System.out.format("name=%s; age=%s", result.name, result.age);

parallel stream

parallelStream不同于Stream的地方是它会让中间流操作利用ForkJoinPool的线程进行操作,而使用的线程是不确定的,代码例子如下:

Arrays.asList("a1", "a2", "b1", "c2", "c1")    .parallelStream()    .filter(s -> {        System.out.format("filter: %s [%s]\n",            s, Thread.currentThread().getName());        return true;    })    .map(s -> {        System.out.format("map: %s [%s]\n",            s, Thread.currentThread().getName());        return s.toUpperCase();    })    .sorted((s1, s2) -> {        System.out.format("sort: %s <> %s [%s]\n",            s1, s2, Thread.currentThread().getName());        return s1.compareTo(s2);    })    .forEach(s -> System.out.format("forEach: %s [%s]\n",        s, Thread.currentThread().getName()));

输出结果:

filter: b1 [main]filter: a1 [ForkJoinPool.commonPool-worker-3]filter: a2 [ForkJoinPool.commonPool-worker-1]map: a2 [ForkJoinPool.commonPool-worker-1]filter: c1 [ForkJoinPool.commonPool-worker-2]forEach: A2 [ForkJoinPool.commonPool-worker-1]map: a1 [ForkJoinPool.commonPool-worker-3]map: b1 [main]forEach: A1 [ForkJoinPool.commonPool-worker-3]filter: c2 [ForkJoinPool.commonPool-worker-1]map: c1 [ForkJoinPool.commonPool-worker-2]map: c2 [ForkJoinPool.commonPool-worker-1]forEach: B1 [main]forEach: C2 [ForkJoinPool.commonPool-worker-1]forEach: C1 [ForkJoinPool.commonPool-worker-2]
0 0