Java8:Stream概念
来源:互联网 发布:怎么联系淘宝官方客服 编辑:程序博客网 时间:2024/05/01 01:37
参考:Java 8 Stream Tutorial
介绍:什么是流、管道/Pipelinin、惰性/ laziness、部分地构造、流的一次性
1.什么是流
- 流 Stream<T>是元素集合的描述,而非列举。因而流的元素可以无限。
- 流 Stream<T>是惰性计算的链表,(高阶函数)filter, map, reduce, find, match, sort等操作,如果不考虑开销,也可以用在Collections上——换言之,这些函数不是什么了不起的东西,而是 Stream<T>的惰性计算,才是要点。
- 流对象只能够被尾函数消费一次
正则表达式(Regular Expression)是一种生成字符串的字符串,例如
String regEx="ab*";
"ab*"——表示了a、ab、abb、abbb……。可以说,正则表达式"ab*"表示了一个无限的集合。
流 Stream<T>表示类型为T的元素的序列,支持对元素的顺序或并发的聚合操作。Java的Collections类似{a、ab、abb、abbb},而流类似"ab*"。
3.5.1 Streams Are Delayed Lists
为Java带来了函数式编程风格的,并非lambda表达式,而是Stream API 。正如SICP所展示的,(高阶函数)filter, map, reduce, find, match, sort等操作,可以建立在list之上(但是开销太大),只有拥有 delayed evaluation /延迟计算/惰性计算的Stream ,才能够发挥上面的高阶函数的威力。
Stream<T>的操作,通常形容为管道/Pipelining,其实,也可以把String的操作称为管道。
List<String> myList = Arrays.asList("a1", "a2", "b1", "c2", "c1", "c0"); myList.stream() .filter(s -> s.startsWith("c")) .map(String::toUpperCase) .sorted() .forEach(System.out::println); boolean b = "a1".toUpperCase().endsWith("1");//比较 流的管道概念。
也可以看一看管道的慢动作:
Stream<String> stream = myList.stream(); Stream<String> filter = stream.filter(s -> s.startsWith("c")); Stream<String> map = filter.map(String::toUpperCase); Stream<String> sorted = map.sorted(); sorted.forEach(System.out::println);
上面代码说明了,流的操作分为两种:
Intermediate /中间的函数:程序中可以多次地、串接地调用流的 intermediate 操作,因为中间操作如映射map、过滤filter函数返回一个流对象。这类操作具有惰性/ laziness。虽然代码写出了对它们 的调用,但是并没有真正执行。
- Stream<T> filter(Predicate<? super T> predicate);
- <R> Stream<R> map(Function<? super T, ? extends R> mapper);
- Stream<T> distinct();
- Stream<T> sorted();
- Stream<T> peek(Consumer<? super T> action);
- Stream<T> limit(long maxSize);
- Stream<T> skip(long n);
- 一些创建Stream<T>的方法,Stream.iterate和generate,作为第一个调用。
Terminal /结尾的函数:对于流的串接调用,只能在最后加一个 terminal函数,正如调用String的 .endsWith("1")。结尾函数返回一个值或void函数产生一个副作用/side effect。 terminal函数的一个作用,在于唤醒前面的懒虫。
- void forEach(Consumer<? super T> action);
- Object[] toArray(); //重载
- Optional<T> reduce(BinaryOperator<T> accumulator); //重载
- <R, A> R collect(Collector<? super T, A, R> collector);
- long count();
- boolean anyMatch(Predicate<? super T> predicate);//相关
- Optional<T> findFirst();//Optional<T> findAny();
延迟计算+管道
流的一个重要特点:流总是部分地构造,并将该部分交给后面的函数处理;如果函数需要更多的数据,流会自动地构造新的数据。换言之,流像挤牙膏一样,一个个元素交给处理函数,给程序员的印象则是整个流的元素都存在。
为了观察流的处理管道,下面在处理函数中添加输出语句。
1.懒虫
public static void m2() { Stream<String> of = Stream.of("c2", "a1", "b0"); Stream<String> map = of.map(s -> { System.out.println("map: " + s);//副作用 return s.toUpperCase(); }); }
public static void m2() { Stream<String> of //同上 System.out.println(map); System.out.println(map.count()); }添加尾函数count(),则输出:
map: c2
map: a1
map: b0
3
如果添加的结尾函数为 forEach,注意:流对象of有3个元素,但是流对象map在懒惰中,开始的时候没有元素。
map.forEach()说:"map给我数据";
流对象map从流对象of中提取一个数据"c2"执行of.map();这时流对象map有了一个元素"C2";
forEach处理"C2";但是,map.forEach()要处理流对象map的所有元素,什么是所有?按照约定,of.map()将of对象的所有元素转换/map()后,形成流对象map的元素,因此,map的所有元素,个数即of对象的所有元素的个数。
public static void m2() { Stream<String> of = Stream.of("c2", "a1", "b0"); Stream<String> map = of.map(s -> { System.out.println("map: " + s);//副作用 return s.toUpperCase(); }); //System.out.println(map); //System.out.println(map.count()); map.forEach(s -> System.out.println("forEach: " + s)); }
于是,输出为:
map: c2forEach: C2
map: a1
forEach: A1
map: b0
forEach: B0
练习:请解释下面代码的输出:(结尾函数找流对象filter,而流对象filter找流对象map 要数据)
public static void m2() { Stream<String> of = Stream.of("c2", "a1","a2", "b0"); Stream<String> map = of.map(s -> { System.out.println("map: " + s);//副作用 return s.toUpperCase(); }); Stream<String> filter = map.filter(s -> { System.out.println("filter: " + s); return s.startsWith("A"); }); filter.forEach(s -> System.out.println("forEach: " + s)); }输出:
map: c2
filter: C2 //流对象filter没有"C2"这个元素
map: a1
filter: A1
forEach: A1
map: a2
filter: A2
forEach: A2
map: b0
filter: B0
ok,如果我们交换上面代码中map()和filter()函数的调用顺序,先filter()再map()(代码见下面),可以减少函数执行的次数。
流的一次性
public static void m3() { Stream<String> map = Stream.of("c2", "a1", "a2", "b0") .filter(s -> s.startsWith("A")) .map(s -> s.toUpperCase()); map.forEach(s -> System.out.println("forEach: " + s)); long count = map.count();//运行时异常 }假设我们在打印流对象map的元素的同时,还要统计其个数,m3()代码抛出异常
java.lang.IllegalStateException: stream has already been operated upon or closed
因为,流对象只能够被尾函数消费一次,或者说,一个流对象只能够调用一次结尾函数。那么如何对一个流完成多次尾操作呢?
//Stream<String> map2 = map; //想都不用想
解决方案有:
- collect 或toArray,再流化。
public static void m3() { String[] toArray = (String[])(Stream.of("c2", "a1", "a2", "b0") .filter(s -> s.startsWith("A")) .map(s -> s.toUpperCase()) .toArray()); Stream.of(toArray) .forEach(s -> System.out.println("forEach: " + s)); long count = Stream.of(toArray) .count(); }
- stream supplier创建两个流
public static void m3() { Supplier<Stream<String>> streamSupplier = () -> Stream.of("c2", "a1", "a2", "b0") .filter(s -> s.startsWith("A")) .map(s -> s.toUpperCase()); streamSupplier.get().forEach(s -> System.out.println("forEach: " + s)); streamSupplier.get().count(); }
- Java8:Stream概念
- Java8 Stream
- java8 stream
- Java8 Stream
- Java8 Stream
- Java8:Stream
- java8-stream
- java8 stream
- Java8 Stream
- Java8 Stream
- Java8 stream
- java8 stream
- java8-stream
- Java8 Stream
- java8新特性(三):Stream流的概念
- Java8---Stream的介绍和相关概念(1)
- Java8---Stream的介绍和相关概念(1)
- Java8 Stream操作
- Linux入门级需要掌握的命令
- nodejs+mongodb
- SpringMVC与iReport(JasperReports) 整合开发实例教程
- MySQL存储过程详解 mysql 存储过程
- LayoutInflater.inflate()三种重载方法参数解释
- Java8:Stream概念
- 29. Divide Two Integers
- mnesia 总结
- Adobe Photoshop CS6无序列号破解方法
- linux-windows碎片问题
- SpringMVC与iReport整合开发时的PDF中文显示处理
- 源码地址汇总
- 什么时候使用存储过程比较适合
- jQuery如何设置复选按钮选中,求解