Java函数式编程-3.流(Stream)

List<String> words = Arrays.asList("hello","world");
 List<String> chars = words.stream()
     .flatMap(word ->  Stream.of(word.split("")))
     .collect(Collectors.toList());

   Jdk8中新增的特性旨在帮助程序员写出更好的代码,其中对核心类库的改进是很关键的一部分。对核心类库的改进主要包括集合类的API和新引入的流(Stream)。流使程序员得以站在更高的抽象层次上对集合进行操做。程序员

1.什么是流(Stream)?

Stream是特定类型的对象造成的一个队列并支持聚合操做。 Java中的Stream并不会存储元素,而是按需计算。流的来源能够是集合,数组,I/O channel, 产生器generator 等。聚合操做相似SQL语句同样的操做, 好比filter, map, reduce, find, match, sorted等。和之前的Collection操做不一样, Stream操做还有两个基础的特征:Pipelining中间操做都会返回流对象自己。 这样多个操做能够串联成一个管道,如同流式风格(fluent style)。 这样作能够对操做进行优化, 好比延迟执行(laziness)和短路( short-circuiting)。
在 Java 8 中, 集合接口有两个方法来生成流:数组

  • stream() − 为集合建立串行流。
  • parallelStream() − 为集合建立并行流。
List<String> list = Arrays.asList("a", "b", "c", "d", null);
List<String> filtered = list.stream().filter(e -> Objects.nonNull(e)).collect(Collectors.toList());
long count = list.parallelStream().filter(e -> Objects.nonNull(e)).count();

2.内部迭代

Java程序员在使用集合时,一个通用的模式时在集合上进行迭代,在迭代过程当中处理每个元素并返回处理结果。一般代码时这样的:函数

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
 int sum = 0;
 Iterator<Integer> iterator = list.iterator();
 while (iterator.hasNext()) {
    sum += iterator.next();
 }
System.out.println(sum);

这段代码自己没什么问题就写起来有点麻烦,就其背后原理来看其实就是一个封装了迭代的语法糖,首先调用iterator方法产生一个新的Iterator对象进而控制整个迭代过程,这就是外部迭代。学习

然而外部迭代的问题在于首先,很难抽象出后续提到的复杂操做,另外从本质上来说只是一种串行化操做。整体来看使用for循环会将行为和方法混为一谈。另外一种方法是内部迭代,和调用iterator()做用同样使用stream()方法,该方法不是返回一个控制迭代的Iterator对象,而是返回内部迭代中的相应接口:Stream。示例以下:优化

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum = list.stream().filter(e -> e > 5).reduce(0, Integer::sum);
System.out.println(sum);

3.实现机制

一般在Java 中调用一个方法计算机会随即执行操做:好比,System.out.println("hello world!")会在终端上输出一条信息。Stream里的一些方法略有不一样,它们虽是普通Java方法可是返回的Stream对象并非一个新的集合,而是建立新集合的配方。ui

list.stream().filter(e -> e > 5);

上述代码并未作什么实际性的工做,filter只是刻画出了Stream可是并无产生新的集合,像filter这样只描述Stream,最终不产生新集合的方法惰性求值方法,而像sum()这样最终从Stream产出值的方法叫作及早求值方法。若是在filter方法中添加一条输出语句就会比较清楚的看出差别。spa

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
list.stream().filter(e -> {
            System.out.println(e);
            return e > 5;
          });

若是将上述语句添加一个及早求值方法就能够看到集合中的元素被打印出来。scala

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
list.stream().filter(e -> {
            System.out.println(e);
            return e > 5;
        }).findFirst();

判断一个操做是惰性求值仍是及早求值很简单,只要返回值是Stream那么是惰性求值。使用这些操做的理想方式就是造成一个惰性求值链,最后用一个及早求值的操做返回想要的结果,这正是他的合理之处。那为何要区分惰性求值和及早求值呢?缘由很简单只须要一次迭代就能够获得所有结果,例如 :上述代码只须要找到第一个大于5的数而不须要比较全部集合元素。设计

4.经常使用的流操做

为了更好的学习Stream,咱们接下来学习一些经常使用的stream操做。code

  •  filter

若是须要在遍历集合元素时清洗非法元素可使用filter方法,咱们在上述讨论过程当中已经使用过该方法。filter接收一个函数做为参数,该函数使用lambda表达式表示,若是被清洗元素符合预期则返回true,其实看源码会发现这个lambda表达式是咱们以前讲过的 Predicate<T>函数。

  • collect

这个方法是由Stream生成一个List,是一个及早求值操做。Jdk8中借鉴了不少Scala的设计思想,Stream的of方法能够用来生成一个新的Stream,咱们来看下边的一个例子。

List<String> list = Stream.of("a", "b", "c","d","e").collect(Collectors.toList());
  • map

map函数是用来说一个类型转换为另一种类型,参数一样是一个lambda表达式对应咱们以前讲过的 Function<T, R> 函数。由于map是一个惰性求值方法咱们配合以前的collect看一个例子。

List<String> list = Stream.of("a", "b", "c").map(e -> e.toUpperCase()).collect(Collectors.toList());
  System.out.println(list);
  • flatMap

和map相似,不一样的是其每一个元素转换获得的是Stream对象,最终会把转换获得的多个Stream链接成一个Stream。

List<String> words = Arrays.asList("hello","world");
 List<String> chars = words.stream()
     .flatMap(word ->  Stream.of(word.split("")))
     .collect(Collectors.toList());

上述代码咱们能够获得输出结果 :[h, e, l, l, o, w, o, r, l, d],若是将flatMap换成map会发现咱们获得了一个List<Stream<String>> ,你们感兴趣能够上手一试。

  • max和min

和以前讲的函数不一样,这两个方法要求参数是一个比较器 Comparator<T> 

Integer min = Stream.of(1,2,3,4,5).min(Integer::compare).get();
System.out.println(min);
Integer max = Stream.of(1,2,3,4,5).max(Integer::compare).get();
System.out.println(max);
  • reduce

该函数能够实现从一组值中生成一个值,在上述例子中用到的 sum、max、min本质上都是reduce操做。Stream的求和结果每一步都将Stream中的元素累加至accumulator,遍历至Stream中的最后一个元素时,accumulator的值就是全部元素的和。咱们看以下例子

Integer sum = Stream.of(1, 2, 3, 4, 5).reduce(0, (acc, e) -> acc + e);
 System.out.println(sum);

这段代码若是你对scala熟悉那么会很容易理解,acc做为累加器存储了中间计算结果,整个lambda函数本质上是咱们以前讲过的BinaryOperator。

相关文章
相关标签/搜索