JDK8的随笔(06)_Aggregate聚合操作之stream的抛砖引玉
来源:互联网 发布:扫描英语翻译软件app 编辑:程序博客网 时间:2024/06/17 00:42
Aggregate 聚合操作
嗯。项目开始小忙碌,最近一直没有更新。不能犯懒。。
JDK8中引进了Lambda表达式,Method Reference方法参照,以及default方法,static方法在interface中的使用。其实,这些也还都是铺垫,虽然说Lambda表达式的概念在JDK8没有出来的时候就开始炒作,但是我认为JDK8最引人注意的应该还是Aggregate 聚合操作的引入以及这个概念带来的一些思维方式的改变。
先不管写法是否丑陋效果是否有强加性,其实Aggregate的出现和实现方式都体现了当下一个简单著名算法的影子:MapReduce。
原创原文:blog.csdn.net/forevervip
下面开始依托javase的文档来粗粗了解一下聚合操作。
管道与数据流 pipelines and streams
在JDK8中,我们的collections后会“点”出来一个stream。
Person[] rosterAsArray = roster.toArray(new Person[roster.size()]); roster .stream()
这个stream即是这次的主角,数据流。
先看一个例子:
假设roster是一个List<Person>的list,那么我们如果循环这个list,可以采用下面的写法
for (Person p : roster) { System.out.println(p.getName());}
而,如果使用stream的数据流写法则:
roster .stream() .forEach(e -> System.out.println(e.getName());
去掉回车的话,其实只有一行:
roster.stream().forEach(e -> System.out.println(e.getName());
stream后紧接着进行了一个forEach的操作,这个操作其实很多弱语言都有,比如javascript。而forEach中调用了一个Lambda表达式,表达式的内容很简单,其实就是一个打印。
forEach方法中使用的参数是:
forEach(Consumer<? super Integer>)
Consumer和上一篇中写到的Supplier类似,都是java.util.function中的函数式接口中的一员。
代码如下:
/* * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */package java.util.function;import java.util.Objects;/** * Represents an operation that accepts a single input argument and returns no * result. Unlike most other functional interfaces, {@code Consumer} is expected * to operate via side-effects. * * <p>This is a <a href="package-summary.html">functional interface</a> * whose functional method is {@link #accept(Object)}. * * @param <T> the type of the input to the operation * * @since 1.8 */@FunctionalInterfacepublic interface Consumer<T> { /** * Performs this operation on the given argument. * * @param t the input argument */ void accept(T t); /** * Returns a composed {@code Consumer} that performs, in sequence, this * operation followed by the {@code after} operation. If performing either * operation throws an exception, it is relayed to the caller of the * composed operation. If performing this operation throws an exception, * the {@code after} operation will not be performed. * * @param after the operation to perform after this operation * @return a composed {@code Consumer} that performs in sequence this * operation followed by the {@code after} operation * @throws NullPointerException if {@code after} is null */ default Consumer<T> andThen(Consumer<? super T> after) { Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; }}
可以看到,这里的accept方法即使我们的Lambda表达式要去实现的方法,这个实现以及方法很简单。不做多的说明。
继续看一个例子:
我们需要一个条件,符合条件的数据再进行打印的处理:
for (Person p : roster) { if (p.getGender() == Person.Sex.MALE) { System.out.println(p.getName()); }}
OK,采用stream的写法如下(一行太长,采用多行的写法):
roster .stream() .filter(e -> e.getGender() == Person.Sex.MALE) .forEach(e -> System.out.println(e.getName()));
其中,forEach和上一个一样,而多出来的是filter的这个中间方法的调用。
可以看到,filter中也是一个Lambda表达式,filter的调用原型:
filter(Predicate<? super T> predicate)
是的,Predicate也是java.util.function的函数式接口,代码内部有兴趣可以去看看源码。
以上的方法调用即为管道聚合操作,这种操作有如下的特点
- 要有一个数据源。一般可以是collection,数组(array),函数生成器(generator function 其实这个的使用可能还是会归结到数组和collection),或者I/O数据通道。例子中我们使用了一个collection。
- 0个或者多个的中间操作过程。例子中我们使用了一个filter,从而生成了一个新的stream数据流。
数据流是一系列的元素,和collection不同的是,它并不作为存储结构来存储数据,而形象点的形容,它就是一个可以传输数据的通道,例子中的Predicate这个函数式接口的方法为boolean test(T t),所以Lambda表达式的e -> e.getGender() == Person.Sex.MALE 当性别是男性的时候可以返回一个true。filter根据返回值会重新生成一个性别都是男性的stream。 - 一个结束操作。这个操作可以做成一个非stream的结果,也可以是一个java的原始数据类型,也可以是一个collection,也或者是例子中的forEach并不返回任何数据。例子中采用的是Lambda表达式打印名字。
再看一个例子:
double average = roster .stream() .filter(p -> p.getGender() == Person.Sex.MALE) .mapToInt(Person::getAge) .average() .getAsDouble();
这个例子中,到filter为止和上面那个例子是一致的。
而mapToInt开始则不同。mapToInt中调用的是ToIntFunction的函数式接口。这里采用的是一个方法参照的写法,其实我们利用e -> e.getAge 也可以代替。之前曾经说过方法参照的几种类型,但是这里稍有不同,后面在说collect方法的时候还会继续说。average()是一个结束操作,类似于一般mapreduce中的reduce的过程,最终getAsDouble会把结果变成一个double类型。
与collection中的迭代操作的不同
首先,使用结束操作动作。
stream不会像collection的迭代那样一个一个利用next去找寻数据,而是利用一个结束操作(方法,函数)。利用这个操作你可以嵌入自己的逻辑来告诉java你需要迭代什么样的数据,但是“迭代”(准确来说不是迭代的简单操作)的过程是JDK帮你做的。以前使用的普通的collection的迭代却是自己既编写逻辑也编写迭代的过程,然后这种迭代只能是线性顺序的迭代算法。新的stream的内部的“迭代”过程打破这个常规,它可以利用多线程来分散计算,把一个大问题切碎成很多小问题,最后合并计算结果,利用并行并发来解决原来的顺序计算,我们后面会深入讨论。
其次,运行过程中是从stream的管道中取得元素,而不是从collection中直接取得元素进行计算。所有计算过程都是一个stream的操作过程。
最后,参数化的方法调用。大多数计算过程中参数可以利用Lambda表达式以及Method Reference来代替,给编写带来了更大的方便及灵活性。
原文原创:blog.csdn.net/forevervip
つづく・・・
- JDK8的随笔(06)_Aggregate聚合操作之stream的抛砖引玉
- JDK8 stream操作
- JDK8的随笔(04)_Lambda表达式扩展之Method References
- jdk8之Stream
- Stream API:简单的聚合方法
- JAVA 8 StreamAPI 和 lambda表达式 总结(四)--stream的一些聚合操作
- JDK8中Array转换成stream的一个坑
- JDK8.0 流stream 基本操作
- Java8的Stream操作
- JDK8的随笔(07)_行云流水般的Lambda表达式
- jdk8 Stream
- MongoDB的聚合操作
- mongotemplate的聚合操作
- JDK8的随笔(02)_Lambda表达式进一步探讨
- JDK8新特性之Stream流
- JDK8的集合流式操作
- Team Stream:体育爱好者的新闻聚合iOS应用
- Stream流的操作案例
- spring之注解(二)Autowired
- UICollectionView 详细讲解学习
- hosts文件配置不生效的解决办法
- Nginx与Apache的Rewrite规则的区别
- Java basic ---Class
- JDK8的随笔(06)_Aggregate聚合操作之stream的抛砖引玉
- 关于android桌面小组件分开更新的的一些问题
- 12.遍历二叉树与二叉树的建立
- 这7个开源技术支撑起整个互联网时代
- 13-语言入门-13-三个数从小到大排序
- 14-语言入门-14-阶乘因式分解(一)
- asp.net 大文件上传
- Android存储之SharedPreferences
- OV9712原理图PADS版本可以直接量产HI3518E配套SENSOR