前言

我们前面说了 lambda表达式,这次我们就将下JDK8的另一个新特性,流(Stream)

stream和lambda‘搭配使用效果更佳,(如果你没有学过lambda表达式,最好先学习下lambda表达式)

看着逼格也更高,也更简洁

我们就拿之前的lambda表达式的举例

我们需要找出集合中所有的 男同学 按照年龄从小到大排序 并且打印出来,我们就这样写

studentList.stream().filter(student -> "男".equals(student.getSex())).sorted((x, y) -> x.getAge()-y.getAge()).forEach(student -> System.out.println(JSON.toJSONString(student, true)));

如果图片链接失效了,可以在我的 github上找到相应文章 jdk8新特性 stream,且更及时更详细,也更有条理。目前已有将近300个star

定义

Stream(流)是一个来自数据源元素队列并支持聚合操作

  • 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
  • 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
  • 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

流的处理流程一般是这样

+--------------------+       +------+   +------+   +---+   +-------+
| stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
+--------------------+       +------+   +------+   +---+   +-------+

聚合操作按照分法可以分成两种:中间操作,终端操作

  • 中间操作

    • 无状态操作

      • filter、map、peek等
    • 有状态操作
      • Sorted、distinct、limit等
  • 终端操作
    • 非短路操作

      • foreach、count、collect等
    • 短路操作
      • findFirst、findAny、anyMatch等

中间操作

中间操作和很多命令像不像我们 sql 里面的命令,你可以理解为我们的那些限制语句,通过这些手段得到我们想要的一些数据

终端操作

顾名思义,就是指最后的操作。一个流里面进行完终端操作之后就不能再进行其他操作了

无状态操作

就是不需要全部遍历完之后才能得到,比如 我上面的代码,我只看这个元素符不符合,不符合我就不要,不需要遍历完全部元素。与此相对,有状态操作就是需要整个集合遍历完才行,比如我们的 sorted,我不遍历完所有元素,我怎么知道哪一个最大,哪一个最小

短路操作

就是找到一个我们就不往下执行了。与此相反,非短路操作也很好理解

各个方法演示

我的集合中有如下元素

private static List<Student> studentList = new ArrayList<Student>() {{add(new Student("张三丰", 20, "男", "体育",180, 75, "太上老君"));add(new Student("张无忌", 18, "男", "语文",178, 73, "文曲星"));add(new Student("赵敏", 17, "女", "数学",170, 50, "太白金星"));add(new Student("金毛狮王", 25, "男", "体育",176, 80, "太白金星"));add(new Student("周芷若", 16, "女", "语文",168, 48, "太上老君"));add(new Student("张三", 21, "男", "英语",172, 65, "如来"));add(new Student("赵勇", 26, "男", "体育",188, 80, "太上老君"));}};

中间操作

无状态操作

filter

filter,就是过滤掉那些不符合你设定的条件的元素

我们看源码

/*** Returns a stream consisting of the elements of this stream that match* the given predicate.** <p>This is an <a href="package-summary.html#StreamOps">intermediate* operation</a>.** @param predicate a <a href="package-summary.html#NonInterference">non-interfering</a>,*                  <a href="package-summary.html#Statelessness">stateless</a>*                  predicate to apply to each element to determine if it*                  should be included* @return the new stream*/Stream<T> filter(Predicate<? super T> predicate);// 再看他的参数,记不记得我当初 讲 lambda 时候讲到的 这个
// Predicate 接口 是输入一个类型,返回一个bool值@FunctionalInterface
public interface Predicate<T> {/*** Evaluates this predicate on the given argument.** @param t the input argument* @return {@code true} if the input argument matches the predicate,* otherwise {@code false}*/boolean test(T t);
}

所以我们使用的时候,返回的是一个bool值,如下所示

Equals 方法返回的是一个bool值

studentList.stream().filter(student -> "男".equals(student.getSex())).forEach(student -> System.out.println(JSON.toJSONString(student, true)));

结果,我们看到它里面已经过滤掉了女同学

{"age":20,"height":180,"name":"张三丰","sex":"男","subject":"体育","teacher":"太上老君","weight":75
}
{"age":18,"height":178,"name":"张无忌","sex":"男","subject":"语文","teacher":"文曲星","weight":73
}
{"age":25,"height":176,"name":"金毛狮王","sex":"男","subject":"体育","teacher":"太白金星","weight":80
}
{"age":21,"height":172,"name":"张三","sex":"男","subject":"英语","teacher":"如来","weight":65
}
{"age":26,"height":188,"name":"赵勇","sex":"男","subject":"体育","teacher":"太上老君","weight":80
}

map

map的作用是,将一个类型的集合转化为另一个类型的集合

我们来看他的源码

    /*** Returns a stream consisting of the results of applying the given* function to the elements of this stream.** <p>This is an <a href="package-summary.html#StreamOps">intermediate* operation</a>.* @return the new stream*/<R> Stream<R> map(Function<? super T, ? extends R> mapper);// 他要传入的是一个 Function 接口,作用是输入一个类型,返回另一个类型@FunctionalInterfacepublic interface Function<T, R> {/*** Applies this function to the given argument.** @param t the function argument* @return the function result*/R apply(T t);}

我们用它生成一个 学生选课的集合。我们输入的是一个Student类型的集合,返回的是一个 String类型的集合

    @Testpublic void mapTest(){studentList.stream().map(student -> student.getSubject()).forEach(student -> System.out.println(JSON.toJSONString(student, true)));}

结果如下

"体育"
"语文"
"数学"
"体育"
"语文"
"英语"
"体育"

faltMap

将一个类型的集合转换成另一个类型的流(注意和map区分)

/*** Returns a stream consisting of the results of replacing each element of* this stream with the contents of a mapped stream produced by applying* the provided mapping function to each element.  Each mapped stream is* {@link java.util.stream.BaseStream#close() closed} after its contents* have been placed into this stream.  (If a mapped stream is {@code null}* an empty stream is used, instead.)** @return the new stream*/<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);// 他也是 Function接口,但是另一个参数是继承自 Stream类的
    /*** 将一个类型的集合抓换成流.我们把一个字符串流转换成一个字符流*/@Testpublic void flatMapTest() {studentList.stream().flatMap(student -> Arrays.stream(student.getName().split(""))).forEach(stu -> System.out.println(JSON.toJSONString(stu,true)));}

peek

peek和foreach很相似,区别是 ,一个是中间操作,一个是终端操作。peek用完之后还能被其他操作进行处理。

/*** Returns a stream consisting of the elements of this stream, additionally* performing the provided action on each element as elements are consumed* from the resulting stream.** @return the new stream*/Stream<T> peek(Consumer<? super T> action);// 我们看到他的函数接口是Consumer,他是输入一个参数,但是不会有返回值
@FunctionalInterface
public interface Consumer<T> {/*** Performs this operation on the given argument.** @param t the input argument*/void accept(T t);
}
    /*** peek 方法*/@Testpublic void peekTest() {studentList.stream().peek(student -> System.out.println(student.getName())).forEach(stu -> System.out.println(JSON.toJSONString(stu,true)));}
张三丰
{"age":20,"height":180,"name":"张三丰","sex":"男","subject":"体育","teacher":"太上老君","weight":75
}
张无忌
{"age":18,"height":178,"name":"张无忌","sex":"男","subject":"语文","teacher":"文曲星","weight":73
}
赵敏
{"age":17,"height":170,"name":"赵敏","sex":"女","subject":"数学","teacher":"太白金星","weight":50
}
金毛狮王
{"age":25,"height":176,"name":"金毛狮王","sex":"男","subject":"体育","teacher":"太白金星","weight":80
}
周芷若
{"age":16,"height":168,"name":"周芷若","sex":"女","subject":"语文","teacher":"太上老君","weight":48
}
张三
{"age":21,"height":172,"name":"张三","sex":"男","subject":"英语","teacher":"如来","weight":65
}
赵勇
{"age":26,"height":188,"name":"赵勇","sex":"男","subject":"体育","teacher":"太上老君","weight":80
}

我们看到 人名和student类是交替打印的**,也就是执行了一次 peek,执行了一次 foreach**,那么每次都是这样吗?不是的,我们来看下一节

有状态操作

sorted

sorted是用来排序的

 /*** Returns a stream consisting of the elements of this stream, sorted* according to the provided {@code Comparator}* @return the new stream*/Stream<T> sorted(Comparator<? super T> comparator);// 他提供了一个 Comparator接口@FunctionalInterfacepublic interface Comparator<T> {/*** Compares its two arguments for order.  Returns a negative integer,* zero, or a positive integer as the first argument is less than, equal* to, or greater than the second.<p>*/int compare(T o1, T o2);
/*** sorted 方法,按照年龄大小排序*/@Testpublic void sortedTest() {studentList.stream().peek(student -> System.out.println(student.getName())).sorted((x,y) -> x.getAge()-y.getAge()).forEach(stu -> System.out.println(JSON.toJSONString(stu,true)));}
张三丰
张无忌
赵敏
金毛狮王
周芷若
张三
赵勇
{"age":16,"height":168,"name":"周芷若","sex":"女","subject":"语文","teacher":"太上老君","weight":48
}
{"age":17,"height":170,"name":"赵敏","sex":"女","subject":"数学","teacher":"太白金星","weight":50
}
{"age":18,"height":178,"name":"张无忌","sex":"男","subject":"语文","teacher":"文曲星","weight":73
}
{"age":20,"height":180,"name":"张三丰","sex":"男","subject":"体育","teacher":"太上老君","weight":75
}
{"age":21,"height":172,"name":"张三","sex":"男","subject":"英语","teacher":"如来","weight":65
}
{"age":25,"height":176,"name":"金毛狮王","sex":"男","subject":"体育","teacher":"太白金星","weight":80
}
{"age":26,"height":188,"name":"赵勇","sex":"男","subject":"体育","teacher":"太上老君","weight":80
}

我们看到,这次是先打印peek的内容,再打印 foreach的内容,为什么会这样呢?

因为之前我们除只有无状态的方法,他不需要遍历完其他全部的操作,所以他就交替打印了,也符合流的特性。但是我们这一次有了有状态的操作,就只能等到处理完全部元素之后能进行foreach的遍历操作

distinct

去重

/*** Returns a stream consisting of the distinct elements (according to* {@link Object#equals(Object)}) of this stream** @return the new stream*/Stream<T> distinct();
/*** distinct 方法,找出所有老师*/
@Test
public void distinctTest() {studentList.stream().map(student -> student.getTeacher()).distinct().forEach(stu -> System.out.println(JSON.toJSONString(stu,true)));
}
"太上老君"
"文曲星"
"太白金星"
"如来"

limit

    /*** Returns a stream consisting of the elements of this stream, truncated* to be no longer than {@code maxSize} in length.** @param maxSize the number of elements the stream should be limited to* @return the new stream* @throws IllegalArgumentException if {@code maxSize} is negative*/Stream<T> limit(long maxSize);   /*** limit 方法,只显示前4个*/@Testpublic void limitTest() {studentList.stream().limit(4).forEach(stu -> System.out.println(JSON.toJSONString(stu,true)));}
{"age":20,"height":180,"name":"张三丰","sex":"男","subject":"体育","teacher":"太上老君","weight":75
}
{"age":18,"height":178,"name":"张无忌","sex":"男","subject":"语文","teacher":"文曲星","weight":73
}
{"age":17,"height":170,"name":"赵敏","sex":"女","subject":"数学","teacher":"太白金星","weight":50
}
{"age":25,"height":176,"name":"金毛狮王","sex":"男","subject":"体育","teacher":"太白金星","weight":80
}

skip

/*** Returns a stream consisting of the remaining elements of this stream* after discarding the first {@code n} elements of the stream.* If this stream contains fewer than {@code n} elements then an* empty stream will be returned.* @throws IllegalArgumentException if {@code n} is negative*/Stream<T> skip(long n);/*** skip 方法,跳过前4个*/@Testpublic void skipTest() {studentList.stream().skip(4).forEach(stu -> System.out.println(JSON.toJSONString(stu,true)));}
{"age":16,"height":168,"name":"周芷若","sex":"女","subject":"语文","teacher":"太上老君","weight":48
}
{"age":21,"height":172,"name":"张三","sex":"男","subject":"英语","teacher":"如来","weight":65
}
{"age":26,"height":188,"name":"赵勇","sex":"男","subject":"体育","teacher":"太上老君","weight":80
}

skip+limit可以完成分页功能

    /*** limit+skip 方法,完成分页功能*/@Testpublic void skipAndLimitTest() {studentList.stream().skip(1 * 4).limit(4).forEach(stu -> System.out.println(JSON.toJSONString(stu, true)));}

终端操作

短路

anyMatch

    /*** Returns whether any elements of this stream match the provided* predicate.  May not evaluate the predicate on all elements if not* necessary for determining the result.  If the stream is empty then* {@code false} is returned and the predicate is not evaluated.* @return {@code true} if any elements of the stream match the provided* predicate, otherwise {@code false}*/boolean anyMatch(Predicate<? super T> predicate);/*** anyMatch方法,判断是否有一个满足条件*/@Testpublic void anyMatchTest() {final boolean b = studentList.stream().peek(student -> System.out.println(student)).allMatch(student -> student.getAge() > 100);System.out.println(b);}

为啥说是短路操作。我们测试一下就知道

我们看到,它只打印了一个元素。因为第一个就不满足,他就不会再往下执行了,直接返回false

Student(name=张三丰, age=20, sex=男, subject=体育, height=180, weight=75, teacher=太上老君)
false

allMatch

 /*** Returns whether all elements of this stream match the provided predicate.* May not evaluate the predicate on all elements if not necessary for* determining the result.  If the stream is empty then {@code true} is* returned and the predicate is not evaluated* @return {@code true} if either all elements of the stream match the* provided predicate or the stream is empty, otherwise {@code false}*/// 我们看到他的接口是一个 Predicate ,这个我们之前介绍过,返回的是一个bool值boolean allMatch(Predicate<? super T> predicate);/*** allMatch方法,判断是否全部满足输入的条件*/@Testpublic void allMatchTest() {final boolean b = studentList.stream().allMatch(student -> student.getAge() < 100);System.out.println(b);}

findFirst

/*** Returns an {@link Optional} describing the first element of this stream,* or an empty {@code Optional} if the stream is empty.  If the stream has* no encounter order, then any element may be returned.* @return an {@code Optional} describing the first element of this stream,* or an empty {@code Optional} if the stream is empty* @throws NullPointerException if the element selected is null*/// 他返回的是一个 Optional对象Optional<T> findFirst();   /*** findFirst方法*/@Testpublic void findFirstTest() {final Optional<Student> first = studentList.stream().peek(student -> System.out.println(student)).findFirst();System.out.println(first.get());}

findAny

/*** Returns an {@link Optional} describing some element of the stream, or an* empty {@code Optional} if the stream is empty.** <p>This is a <a href="package-summary.html#StreamOps">short-circuiting* terminal operation</a>.** <p>The behavior of this operation is explicitly nondeterministic; it is* free to select any element in the stream.  This is to allow for maximal* performance in parallel operations; the cost is that multiple invocations* on the same source may not return the same result.  (If a stable result* is desired, use {@link #findFirst()} instead.)** @return an {@code Optional} describing some element of this stream, or an* empty {@code Optional} if the stream is empty* @throws NullPointerException if the element selected is null* @see #findFirst()*/// 注意 他的解释中说了,findAny可以发挥并行操作的性能,但是如果你在并行的时候想要一个稳定的结果,要用 findFirst。Optional<T> findAny();/*** 因为我们使用的是串行的操作,所以并不影响结果,和findFirst 的结果一样*/@Testpublic void findAnyTest() {final Optional<Student> first = studentList.stream().peek(student -> System.out.println(student)).findAny();System.out.println(first.get());}

max

/*** Returns the maximum element of this stream according to the provided* {@code Comparator}.  This is a special case of a* <a href="package-summary.html#Reduction">reduction</a>.** <p>This is a <a href="package-summary.html#StreamOps">terminal* operation</a>.** @param comparator a <a href="package-summary.html#NonInterference">non-interfering</a>,*                   <a href="package-summary.html#Statelessness">stateless</a>*                   {@code Comparator} to compare elements of this stream* @return an {@code Optional} describing the maximum element of this stream,* or an empty {@code Optional} if the stream is empty* @throws NullPointerException if the maximum element is null*/// 返回的是一个 Optional类Optional<T> max(Comparator<? super T> comparator);/*** max 方法测试,输出最大的年龄。如果从这里点进入就是使用的 intStream接口,和之前的还不一样*/@Testpublic void maxTest() {final OptionalInt max = studentList.stream().mapToInt(stu -> stu.getAge()).max();System.out.println(max.getAsInt());}

终端非短路

collect

    @Testpublic void collectTest(){final List<Student> list = studentList.stream().filter(student -> "女".equals(student.getSex())).collect(Collectors.toList());System.out.println(JSON.toJSONString(list,true));}
[{"age":17,"height":170,"name":"赵敏","sex":"女","subject":"数学","teacher":"太白金星","weight":50},{"age":16,"height":168,"name":"周芷若","sex":"女","subject":"语文","teacher":"太上老君","weight":48}
]

groupBy 进行分类

/*** Returns a {@code Collector} implementing a "group by" operation on* input elements of type {@code T}, grouping elements according to a* classification function, and returning the results in a {@code Map}.** <p>The classification function maps elements to some key type {@code K}.* The collector produces a {@code Map<K, List<T>>} whose keys are the* values resulting from applying the classification function to the input* elements, and whose corresponding values are {@code List}s containing the* input elements which map to the associated key under the classification* function.*/// 可以看到,他的参数也是一个 Function接口,public static <T, K> Collector<T, ?, Map<K, List<T>>>groupingBy(Function<? super T, ? extends K> classifier) {return groupingBy(classifier, toList());}@Testpublic void collectTest(){
//        final List<Student> list = studentList.stream()
//                .filter(student -> "女".equals(student.getSex()))
//                .collect(Collectors.toList());// 按照性别分类final Map<String, List<Student>> list = studentList.stream().collect(Collectors.groupingBy(s -> s.getSex()));System.out.println(JSON.toJSONString(list,true));}
{"女":[{"age":17,"height":170,"name":"赵敏","sex":"女","subject":"数学","teacher":"太白金星","weight":50},{"age":16,"height":168,"name":"周芷若","sex":"女","subject":"语文","teacher":"太上老君","weight":48}],"男":[{"age":20,"height":180,"name":"张三丰","sex":"男","subject":"体育","teacher":"太上老君","weight":75},{"age":18,"height":178,"name":"张无忌","sex":"男","subject":"语文","teacher":"文曲星","weight":73},{"age":25,"height":176,"name":"金毛狮王","sex":"男","subject":"体育","teacher":"太白金星","weight":80},{"age":21,"height":172,"name":"张三","sex":"男","subject":"英语","teacher":"如来","weight":65},{"age":26,"height":188,"name":"赵勇","sex":"男","subject":"体育","teacher":"太上老君","weight":80}]
}Process finished with exit code 0

JDK8 stream流的骚操作相关推荐

  1. Stream流的收集操作

    概念 对数据使用Stream流的方式操作完毕后,可以把流中的数据收集到集合中. 常用方法 方法名 说明 R collect(Collector collector) 把结果收集到集合中 工具类Coll ...

  2. java 合并流_Java Stream 流实现合并操作示例

    本文实例讲述了Java Stream 流实现合并操作.分享给大家供大家参考,具体如下: 1. 前言 Java Stream Api提供了很多有用的 Api 让我们很方便将集合或者多个同类型的元素转换为 ...

  3. Java中Stream流的中间操作步骤

    Stream流的中间操作 前提 public static List<Emploeer> getEmploer(){List<Emploeer> list = new Arra ...

  4. Java中的Stream流以及收集操作

    一.Stream流的生成方式 1.Stream流的使用: <1>:生成流:通过数据源(集合.数组等)生成流 <2>:中间操作:一个流后面可以跟随零个或多个中间操作,其目的主要是 ...

  5. java jdk8 Stream流操作学习

    Stream流有一些特性: Stream流不是一种数据结构,不保存数据,它只是在原数据集上定义了一组操作. 这些操作是惰性的,即每当访问到流中的一个元素,才会在此元素上执行这一系列操作. Stream ...

  6. Java8中Stream流对集合操作

    java8中Stream流引入函数式编程思想,主要配合各种接口.lambda表达式.方法引用等方式,为集合的遍历.过滤.映射等提供非常"优雅"的操作方式. Student.java ...

  7. [Java基础]Stream流的收集操作

    代码如下: package CollectPack;import java.util.*; import java.util.stream.Collectors; import java.util.s ...

  8. JDK1.8 升级这么久!Stream 流的规约操作有哪些?

    前段时间介绍了部分 Stream常见接口方法,理解起来并不困难,但Stream的用法不止于此,本节我们将仍然以Stream为例,介绍流的规约操作.  规约操作又被称作折叠操作,是通过某个连接动作将所 ...

  9. java8新特性 Stream流的优雅操作

    1. 使用传统的方式,遍历集合,对集合中的数据进行过滤 //创建一个List集合,存储姓名List<String> list = new ArrayList<>();list. ...

最新文章

  1. nyoj-492 King(状态压缩)
  2. HDU4135 HDU2841 HDU1695 求[1,m]中与n互素的数的个数
  3. 找工作的迷茫期开始了
  4. Android 应用开发(8)---语言和语言区域
  5. linux查看系统的版本信息失败,Linux - 查看系统的版本信息
  6. Delphi7 如何调整背景色为黑色容易护眼
  7. CAD坐标怎么输入?CAD坐标标注教程
  8. JAVA时间格式化处理_java时间格式化处理
  9. NBSI2内部功能实现大揭谜
  10. 迅捷新版PDF转换器
  11. 《图算法》第五章 中心性算法-2
  12. SQL Server 由于一个或多个对象访问此列,ALTER TABLE DROP COLUMN xxx 失败问题解决
  13. 计算机领域的所有SCI一区期刊
  14. 一文看懂Python的多进程通信
  15. obs多推流地址_微信小程序直播电脑端OBS推流直播教程
  16. Sentinel流控效果—Warm Up
  17. Linux 内核编译配置选项简介(转)
  18. 一个菜鸟实习生的月总结
  19. 小区网络视频监控系统解决方案
  20. 微信小程序 JS 遍历对象的属性和值

热门文章

  1. 视频教程-Spring Boot实战入门视频课程-Java
  2. CentOS7 忘记登陆密码 修改root密码
  3. 使用EasyExcel将本地excel数据读取后导入mysql数据库中
  4. 小米 11 ultra旗舰版官方原版ROM系统MIUI12.5所有固件
  5. 关于JVM中Eden区、Survivor from区和Survivor to区的理解
  6. 计算机应用中兴5G通讯是什么,中兴通讯自曝5G成绩单:承建全国三成以上5G网络...
  7. 在国内市场,外汇到底能不能做?外汇合法吗?
  8. Linux下重启磁盘盘符会变,linux系统 重启盘符错乱问题
  9. kubernets(四)创建高可用集群
  10. 在xampp中访问html,怎么控制远程访问xampp