java8-Stream流【转】

Stream是Java 8中引入的一个新的抽象层。 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势 Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。获取一个数据源(source)→ 数据转换→执行操作获取想要的结果,每次转换原有 Stream 对象不改变,返回一个新的 Stream 对象(可以有多 使用流,可以以类似于SQL语句的声明方式来处理数据。例如,以下SQL语句 SELECT max(salary),employee_id,employee_name FROM Employee 上述SQL表达式自动返回最高受薪雇员的详细信息,而不需要客户端做任何事情。在Java中使用集合框架,开发人员必须使用循环并进行重复检查。 另一个问题是效率;由于现在的电脑基本都是多核处理器,因此Java开发人员可以编写并行代码处理,但是往往会出错 为了解决这些问题,Java 8引入了流的概念,让开发人员以声明方式处理数据,并利用多核架构,而无需为其编写任何特定的代码。 (代码简洁+多核处理) stream并不是某种数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。获取一个数据源(source)→ 数据转换→执行操作获取想要的结果,每次转换原有 Stream 对象不改变,返回一个新的 Stream 对象(可以有多次转换),这就允许对其操作可以像链条一样排列,变成一个管道,它只是数据源的一种视图。这里的数据源可以是一个数组,Java容器或I/O channel等。正因如此要得到一个stream通常不会手动创建,而是调用对应的工具方法,比如:
  • 调用Collection.stream()或者Collection.parallelStream()方法
  • 调用Arrays.stream(T[] array)方法
常用的四种stream接口继承关系如下图: 图中4种stream接口继承自BaseStream,其中IntStream, LongStream, DoubleStream对应三种基本类型(int, long, double,注意不是包装类型),Stream对应所有剩余类型的stream视图。为不同数据类型设置不同stream接口,可以1.提高性能,2.增加特定接口函数 你可能会奇怪为什么不把IntStream等设计成Stream的子接口?毕竟这接口中的方法名大部分是一样的。答案是这些方法的名字虽然相同,但是返回类型不同,如果设计成父子接口关系,这些方法将不能共存,因为Java不允许只有返回类型不同的方法重载。 虽然大部分情况下stream是容器调用Collection.stream()方法得到的,但stream和collections有以下不同:
  • 无存储。stream不是一种数据结构,它只是某种数据源的一个视图,数据源可以是一个数组,Java容器或I/O channel等。
  • 为函数式编程而生。对stream的任何修改都不会修改背后的数据源,比如对stream执行过滤操作并不会删除被过滤的元素,而是会产生一个不包含被过滤元素的新stream。(重新生成+副本)
  • 惰式执行。stream上的操作并不会立即执行,只有等到用户真正需要结果的时候才会执行。(中间操作和结束操作)
  • 可消费性。stream只能被“消费”一次,一旦遍历过就会失效,就像容器的迭代器那样,想要再次遍历必须重新生成。
count = strings.stream().filter(string -> string.isEmpty()).count();//filter里面是定义好的规则 其参数就是predicate
System.out.println("Empty Strings: " + count);
 
count = strings.stream().filter(string -> string.length() == 3).count();
System.out.println("Strings of length 3: " + count);
每次使用都是重新调用stream() 对stream的操作分为为两类,中间操作(intermediate operations)和结束操作(terminal operations),二者特点是:
  1. 中间操作总是会惰式执行,调用中间操作只会生成一个标记了该操作的新stream,仅此而已。
  2. 结束操作会触发实际计算,计算发生时会把所有中间操作积攒的操作以pipeline的方式执行,这样可以减少迭代次数。计算完成之后stream就会失效。下表汇总了Stream接口的部分常见方法:
操作类型 接口方法
中间操作 concat() distinct() filter() flatMap() limit() map() peek() skip() sorted() parallel() sequential() unordered()
结束操作 allMatch() anyMatch() collect() count() findAny() findFirst() forEach() forEachOrdered() max() min() noneMatch() reduce() toArray()
  为什么不在集合类实现这些操作,而是定义了全新的Stream API?Oracle官方给出了几个重要原因: 一是集合类持有的所有元素都是存储在内存中的,非常巨大的集合类会占用大量的内存,而Stream的元素却是在访问的时候才被计算出来,这种“延迟计算”的特性有点类似Clojure的lazy-seq,占用内存很少。 二是集合类的迭代逻辑是调用者负责,通常是for循环,而Stream的迭代是隐含在对Stream的各种操作中,例如map()。 对于基本数值型,目前有三种对应的包装类型 Stream:IntStream、LongStream、DoubleStream。 关键字: filter:是一个中间操作,接受一个predicate接口类型的变量,并将所有流对象中的元素进行过滤。filter(s -> s.getState()==State.pay) map:是一个对于流对象的中间操作,通过给定的方法,它能够把流对象中的每一个元素对应到另外一个对象上。map(s -> s.getPlanNo()) / map(s -> Plan::planNo)  /  价格变成    10倍 map(s -> s.getPrice().multiply(BigDecimal.valueOf(10))) reduce:把 Stream 元素组合起来。它提供一个起始值(种子),然后依照运算规则(BinaryOperator),返回单个的结果值,并且reduce操作每处理一个元素总是创建一个新值 BigDecimal total = stream().reduce(BigDecimal.zero, (a,b) -> a.add(b));   或 BigDecimal total = stream().reduce(BigDecimal.ZERO, BigDecimal::add) limit : 返回 Stream 的前面 n 个元素;skip 则是扔掉前 n 个元素 sorted: 一个中间操作,能够返回一个排过序的流对象的视图。流对象中的元素会默认按照自然顺序进行排序,除非你自己指定一个Comparator接口来改变排序规则. collect: 修改现存的值 Collectors 类的主要作用就是辅助进行各类有用的 reduction 操作 groupingBy  按规则分组:stream().collect(Collectors.groupingBy(p->p.getState())) partitioningBy 是一种特殊的 groupingBy,它依照条件测试的是否两种结果来构造返回的数据结构,get(true) 和 get(false) 能即为全部的元素对象。 Stream 有三个 match 方法,从语义上说: allMatch:Stream 中全部元素符合传入的 predicate,返回 true anyMatch:Stream 中只要有一个元素符合传入的 predicate,返回 true noneMatch:Stream 中没有一个元素符合传入的 predicate,返回 true
posted @ 2018-08-10 17:59  zohy  阅读(114)  评论(0编辑  收藏  举报