Java 8 新特性之Stream API

1. 概述

1.1 简介

Java 8 中有两大最为重要的改革,第一个是 Lambda 表达式,另外一个则是 Stream API(java.util.stream.*)。

Stream 是 Java 8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用 Stream API 对集合数据进行,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

1.2 流(Stream)到底是什么呢?

是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,流讲的是计算”

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

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

和以前的 Collection 操作不同, Stream 操作还有两个基础的特征:

  • Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
  • 内部迭代: 以前对集合遍历都是通过 Iterator 或者 For-Each 的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

注意 :

  1. Stream 自己不会存储元素
  2. Stream 不会改变源对象,相反,它们会返回一个持有结果的新 Stream
  3. Stream 操作时延迟执行的。这意味着它们会等到需要结果的时候才执行

1.3 Stream 操作的三个步骤

  1. 创建 Stream

    • 一个数据源(集合、数组等),获取一个流
  2. 中间操作(聚合操作)

    • 一个中间操作链,对数据源的数据进行处理
  3. 终止操作(终端操作)

    • 一个终止操作,执行中间操作链,并产生结果

Java Stream

2. 创建 Stream(流)

在 Java 8 中, 集合接口有两个方法来生成流:

  • stream() − 为集合创建串行流。
  • parallelStream() − 为集合创建并行流。

创建 Stream 的 5 种方式

@Test
public void t1() {// 1. Collection 提供了两个方法  stream() 与 parallelStream()List<String> list = new ArrayList<>();Stream<String> stream = list.stream(); //获取一个顺序流Stream<String> parallelStream = list.parallelStream(); //获取一个并行流// 2. 通过 Arrays 中的 stream() 获取一个数组流Integer[] nums = new Integer[10];Stream<Integer> stream1 = Arrays.stream(nums);// 3. 通过 Stream 类中静态方法 of()Stream<Integer> stream2 = Stream.of(1,2,3,4,5,6);// 4. 创建无限流 - 迭代Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2).limit(20);stream3.forEach(System.out::println);// 5. 创建无限流 - 生成Stream<Double> stream4 = Stream.generate(Math::random).limit(5);stream4.forEach(System.out::println);
}

3. Stream 的中间操作

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何处理,而是在终止操作时一次性全部处理,称为“惰性求值”

提供基础的操作数据

List<Employee> emps = Arrays.asList(new Employee(1, "a1", 28, 3888.99),new Employee(2, "a2", 49, 336.66),new Employee(3, "a3", 18, 3323.33),new Employee(4, "a4", 38, 6666.77),new Employee(5, "a5", 8, 80.88),new Employee(5, "a5", 8, 80.88),new Employee(5, "a5", 8, 80.88),new Employee(6, "a6", 56, 100.66)
);

3.1 筛选与切片

  • filter 接收Lambda,从流中排除某些元素。
  • limit 截断流,使元素不超过给定数量
  • skip(n) 跳过元素,返回一个扔掉了前 n 个元素的流,若流中元素不足 n 个,则返回一个空流,与 limit 互补
  • distinct 筛选去重,通过流所生成元素的 hashCode()equals() 去除重复元素

1. filter 接收Lambda,从流中排除某些元素

@Test
public void t2() {// 中间操作:不会执行任何操作Stream<Employee> stream = emps.stream().filter((e) -> {System.out.println("中间操作");return e.getAge() > 20;});// 终止操作:一次性执行全部内容,即"惰性求值"stream.forEach(System.out::println);}

2. limit 截断流

@Test
public void t3() {emps.stream().filter((e) -> {// 当达到 limit 为 2 时将不继续遍历,称为短路,以提高效率System.out.println("短路");return e.getSalary() > 3000;}).limit(2).forEach(System.out::println);
}

3. skip 跳过元素

@Test
public void t4() {emps.stream().filter(e -> e.getSalary() > 100).skip(2).forEach(System.out::println);
}

4. distinct 筛选

@Test
public void t5() {emps.stream().distinct().forEach(System.out::println);
}

要使用 distinct 需要重写 EmployeehashCode()equals() 方法

@Override
public int hashCode() {final int prime = 31;int result = 1;result = prime * result + age;result = prime * result + id;result = prime * result + ((name == null) ? 0 : name.hashCode());long temp;temp = Double.doubleToLongBits(salary);result = prime * result + (int) (temp ^ (temp >>> 32));return result;
}@Override
public boolean equals(Object obj) {if (this == obj)return true;if (obj == null)return false;if (getClass() != obj.getClass())return false;Employee other = (Employee) obj;if (age != other.age)return false;if (id != other.id)return false;if (name == null) {if (other.name != null)return false;} else if (!name.equals(other.name))return false;if (Double.doubleToLongBits(salary) != Double.doubleToLongBits(other.salary))return false;return true;
}

3.2 映射

  • map 接收 Lambda,将元素转换成其它形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
  • flatMap 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流

1. map

将原有的元素进过函数处理,让后映射(覆盖)成一个新的元素

@Test
public void t6() {List<String> list = Arrays.asList("aa","bb","cc","dd");list.stream().map((s) -> s.toUpperCase()).forEach(System.out::println);System.out.println("------------------------------------");emps.stream().map(Employee::getName).forEach(System.out::println);
}

2. flatMap

基础方法

/*** 将字符串分解成字符 list,并返回 Stream* * @param str 待分解字符串* @return Stream*/
public static Stream<Character> filterCharacter(String str) {List<Character> list = new ArrayList<>();for (Character ch : str.toCharArray()) {list.add(ch);}return list.stream();
}

正常情况下,当 filterCharacter 返回的也是一个 Stream 时,相当于流里面还有子流,接收的结果就是 Stream<Stream<Character>>,如果我们要进行遍历的话,就需要使用两层 forEach 才能遍历完成。

@Test
public void t7() {List<String> list = Arrays.asList("aa","bb","cc","dd");Stream<Stream<Character>> stream = list.stream().map(StreamTest1::filterCharacter);// 因为 Stream 还是 Stream 所以需要嵌套 forEach 才能进行遍历stream.forEach((sm) -> {sm.forEach(System.out::println);});
}

但如果使用 flatMap 就可以将每个子流都合并成一个流,这样遍历的时候只使用一层 forEach 就可以了

@Test
public void t8() {List<String> list = Arrays.asList("aa","bb","cc","dd");Stream<Character> stream = list.stream().flatMap(StreamTest1::filterCharacter);stream.forEach(System.out::println);
}

3.3 排序

  • sorted 自然排序(Comparable)
  • sorted(Comparator com) 定制排序(Comparator)

1. sorted 自然排序

@Test
public void t9() {List<String> list = Arrays.asList("cc","aa","dd","bb");list.stream().sorted().forEach(System.out::println);
}

2. sorted(Comparator com) 定制排序

@Test
public void t10() {emps.stream().sorted((e1,e2) -> Integer.compare(e1.getAge(),e2.getAge())).forEach(System.out::println);
}

4. Stream 终止操作

4.1 查找与匹配

  • allMatch 检查是否匹配所有元素
  • anyMatch 检查是否至少匹配一个元素
  • noneMatch 检查是否没有匹配的元素
  • findFirst 返回第一个元素
  • findAny 返回当前流中的任意元素
  • count 返回流中元素的总个数
  • max 返回流中最大值
  • min 返回流中最小值

基础数据

List<Employee> emps = Arrays.asList(new Employee(1, "a1", 28, 3888.99, Employee.Status.BUSY),new Employee(2, "a2", 49, 336.66, Employee.Status.FREE),new Employee(3, "a3", 18, 3323.33, Employee.Status.VOCATION),new Employee(4, "a4", 38, 6666.77, Employee.Status.FREE),new Employee(5, "a5", 8, 80.88, Employee.Status.VOCATION),new Employee(6, "a6", 56, 100.66, Employee.Status.BUSY)
);

1. allMatch 检查是否匹配所有元素

@Test
public void t1() {boolean bool = emps.stream().allMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));System.out.println(bool);
}

2. anyMatch 检查是否至少匹配一个元素

@Test
public void t2() {boolean bool = emps.stream().anyMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));System.out.println(bool);
}

3. noneMatch 检查是否没有匹配的元素

@Test
public void t3() {boolean bool = emps.stream().noneMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));System.out.println(bool);
}

4. findFirst 返回第一个元素

@Test
public void t4() {Optional<Employee> op = emps.stream().sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())).findFirst();System.out.println(op.get());
}

5. findAny 返回当前流中的任意元素

@Test
public void t5() {Optional<Employee> op = emps.stream().filter((e) -> e.getStatus().equals(Employee.Status.FREE)).findAny();System.out.println(op.get());
}

6. count 返回流中元素的总个数

/*** 查询空闲人数*/
@Test
public void t6() {Long count = emps.stream().filter((e) -> e.getStatus().equals(Employee.Status.FREE)).count();System.out.println("count : " + count);
}

7. max 返回流中最大值

/*** 查询工资最高的人*/
@Test
public void t7() {Optional<Employee> op = emps.stream().max((e1,e2) -> Double.compare(e1.getSalary(),e2.getSalary()));System.out.println(op.get());
}

8. min 返回流中最小值

/*** 获取工资最少的人的工资*/
@Test
public void t8() {Optional<Double> op = emps.stream().map(Employee::getSalary).min(Double::compare);System.out.println(op.get());
}

4.2 规约(reduce)

  • T reduce(T identity, BinaryOperator<T> accumulator) 可以将流中的元素反复结合起来,得到一个值,返回 T
  • Optional<T> reduce(BinaryOperator<T> accumulator) 可以将流中的元素反复结合起来,得到一个值,返回 Optional<T>
备注:map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它来进行网络搜索而出名

1. 实例

@Test
public void t9() {List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);Integer sum = list.stream().reduce(0, (x, y) -> x + y);System.out.println("sum : " + sum);
}
说明 :
首先将起始值 0 给 x,然后在流中取出一个元素 1 给了 y,然后 x y 相加结果为 1,再赋给 x,然后再取出一个元素 2 赋给y,然后 x y 相加结果为 3,以此类推

2. 实例

/*** 计算所有人工资的总和*/
@Test
public void t10() {Optional<Double> op = emps.stream().map(Employee::getSalary).reduce(Double::sum);System.out.println("Salary Sum : " + op.get());
}

4.3 收集(collect)

  • collect 将流转换为其它形式,接收一个 Collector(收集器) 接口的实现,用于给 Stream 中元素做汇总的方法

Collector 接口中方法的实现决定了如何对流执行收集操作(如收集到List、Set、Map)。但是 Collector 实现类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:

1. 实例 - 将结果收集到 List、Set 等容器

@Test
public void t1() {List<String> list = emps.stream().map(Employee::getName).collect(Collectors.toList());list.forEach(System.out::println);System.out.println("------------------------------------------");Set<String> set = emps.stream().map(Employee::getName).collect(Collectors.toSet());set.forEach(System.out::println);System.out.println("------------------------------------------");HashSet<String> hs = emps.stream().map(Employee::getName).collect(Collectors.toCollection(HashSet::new));hs.forEach(System.out::println);
}

2. 实例 - 计算

@Test
public void t2() {Long count = emps.stream().collect(Collectors.counting());System.out.println("总数 : " + count);System.out.println("------------------------------------------");Double avg = emps.stream().collect(Collectors.averagingDouble(Employee::getSalary));System.out.println("工资平均值 : " + avg);System.out.println("------------------------------------------");Double sum = emps.stream().collect(Collectors.summingDouble(Employee::getSalary));System.out.println("工资总和 : " + sum);System.out.println("------------------------------------------");Optional<Employee> max = emps.stream().collect(Collectors.maxBy((e1,e2) -> Double.compare(e1.getSalary(),e2.getSalary())));System.out.println("工资最多的员工 : " + max.get());System.out.println("------------------------------------------");Optional<Double> min = emps.stream().map(Employee::getSalary).collect(Collectors.minBy(Double::compare));System.out.println("工资最少的员工 : " + min.get());}

3. 实例 - 计算的另一种实现方式

@Test
public void t6() {DoubleSummaryStatistics dss = emps.stream().collect(Collectors.summarizingDouble(Employee::getSalary));System.out.println("sum : " + dss.getSum());System.out.println("max : " + dss.getMax());System.out.println("avg : " + dss.getAverage());System.out.println("count : " + dss.getCount());System.out.println("min : " + dss.getMin());
}

4.4 分组

分组就相当于 SQL 语句中的 group by,按一个类别或多个类别进行分组

1. 实例

@Test
public void t3() {Map<Employee.Status, List<Employee>> map = emps.stream().collect(Collectors.groupingBy(Employee::getStatus));// 格式化输出,方便查看Gson gson = new GsonBuilder().setPrettyPrinting().create();System.out.println(gson.toJson(map));
}

2. 实例 多级分组

 @Test
public void t4() {Map<Employee.Status, Map<String, List<Employee>>> map = emps.stream().collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {if (e.getAge() <= 35) {return "青年";} else if (e.getAge() <= 50) {return "中年";} else {return "老年";}})));// 格式化输出,方便查看Gson gson = new GsonBuilder().setPrettyPrinting().create();System.out.println(gson.toJson(map));
}

4.5 分区

分区是一种特殊的分组,结果 map 至少包含两个不同的分组一个true,一个false

@Test
public void t5() {Map<Boolean,List<Employee>> map = emps.stream().collect(Collectors.partitioningBy((e) -> e.getSalary() > 1000));// 格式化输出,方便查看Gson gson = new GsonBuilder().setPrettyPrinting().create();System.out.println(gson.toJson(map));
}

4.6 连接

将结果进行连接

@Test
public void t8() {String s1 = emps.stream().map(Employee::getName).collect(Collectors.joining());System.out.println("连接 : " + s1);String s2 = emps.stream().map(Employee::getName).collect(Collectors.joining(","));System.out.println("添加中间分隔符 : " + s2);String s3 = emps.stream().map(Employee::getName).collect(Collectors.joining(",", "==", "=="));System.out.println("添加左右分隔符 : " + s3);
}
本文首发于凌风博客:Java 8 新特性之Stream API
作者:凌风

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/388848.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[Python设计模式] 第17章 程序中的翻译官——适配器模式

github地址:https://github.com/cheesezh/python_design_patterns 适配器模式 适配器模式&#xff0c;将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作[DP]。 当系统的数据和行为都正确&#xff0c;但是接口…

Ubuntu中NS2安装详细教程

前言&#xff1a; NS2是指 Network Simulator version 2&#xff0c;NS&#xff08;Network Simulator&#xff09; 是一种针对网络技术的源代码公开的、免费的软件模拟平台&#xff0c;研究人员使用它可以很容易的进行网络技术的开发&#xff0c;而且发展到今天&#xff0c;它…

es6核心特性图

转载于:https://juejin.im/post/5c19e188e51d452db4753925

带你利用一句话完成转场动画

这篇文章主要给大家介绍了关于iOS如何利用一句话完成转场动画的相关资料&#xff0c;文中通过示例代码介绍的非常详细&#xff0c;对大家的学习或者工作具有一定的参考学习价值&#xff0c;需要的朋友们下面来一起学习学习吧前言本文介绍SS_AnimationTransition 的使用方法,利用…

14.vue路由脚手架

一.vue路由&#xff1a;https://router.vuejs.org/zh/ 1、定义 let router new VueRouter({mode:"history/hash",base:"基本路径" 加一些前缀 必须在history模式下有效linkActiveClass:"active", 范围选择linkExactActiveClass:"exact&qu…

工程师、产品经理、数据工程师是如何一起工作的?

做为一名工程师&#xff0c;免不了与产品经理打交道&#xff0c;如果公司大一些&#xff0c;数据量多一些&#xff0c;还会有数据工程师这个角色。今天会和你主要聊一聊在工作中&#xff0c;产品经理和数据工程师在哪些方面对我们工程师的帮助最大&#xff0c;以及我从他们身上…

linux-buff/cache过大导致内存不足-程序异常

2019独角兽企业重金招聘Python工程师标准>>> 问题描述 Linux内存使用量超过阈值&#xff0c;使得Java应用程序无可用内存&#xff0c;最终导致程序崩溃。即使在程序没有挂掉时把程序停掉&#xff0c;系统内存也不会被释放。 找原因的过程 这个问题已经困扰我好几个月…

Android 适配(一)

一、Android适配基础参数1.常见分辨率&#xff08;px&#xff09;oppx 2340x1080oppR15 2280x1080oppor11sp 2160*10801080*1920 (主流屏幕16&#xff1a;9)1080*216018:9 手机主流分辨率&#xff1a; 1080*2160高端 16:9 手机主流分辨率&#xff1a; 1080P (1080*1920) 或 2K …

Source Insight 创建工程(linux-2.6.22.6内核源码)

1. 软件设置 安装完Source Insight&#xff0c;需要对其进行设置添加对“.S”汇编文件的支持&#xff1a; 2. 新建linux-2.6.22.6工程 1&#xff09;选择工程存放的路径&#xff1a; 2&#xff09;下载linux-2.6.22.6内核源码&#xff0c;并解压。在Source Insight中 指定源码的…

课时20:内嵌函数和闭包

目录&#xff1a; 一、global关键字 二、内嵌函数 三、闭包 四、课时20课后习题及答案 ******************** 一、global关键字 ******************** 全局变量的作用域是整个模块&#xff08;整个代码段&#xff09;&#xff0c;也就是代码段内所有的函数内部都可以访问到全局…

从零开始学产品第六篇:更强大的测试,自动化测试和性能测试

本篇为【从零开始学产品】系列课第1章第5节欢迎到公众号菜单栏&#xff0c;获取产品经理课程更多资料 “测试就是拿点鼠标在电脑上瞎点&#xff0c;或者是用手机随便戳几下么&#xff1f;” “不&#xff0c;是有计划有意图的测试&#xff0c;比如说&#xff0c;边界测试&#…

Get 了滤镜、动画、AR 特效,速来炫出你的短视频开发特技!

在滤镜美颜、搞怪特效、炫酷场景等各种新奇玩法驱动下&#xff0c;短视频开始让人上瘾。 12 月 3 日&#xff0c;七牛云联合八大短视频特效平台共同推出了中国短视频开发者创意大赛&#xff08;China Short Video Contest&#xff09;&#xff0c;面向全国邀请广大开发者&#…

匿名函数、冒泡排序,二分法, 递归

匿名函数 lambda 匿名函数 格式 lambda 参数&#xff1a;返回值 函数名统一叫lambda&#xff0c;最多只能写一行普通的正常的函数 def func(n):return n * n lambda匿名函数写法 a lambda n : n**2 print(a(3)) 当有多个返回值时suiyi lambda x, y : (1, 2) # 笔试题 …

Redis源码剖析

Redis源码剖析和注释&#xff08;一&#xff09;---链表结构 Redis源码剖析和注释&#xff08;二&#xff09;--- 简单动态字符串 Redis源码剖析和注释&#xff08;三&#xff09;--- Redis 字典结构 Redis源码剖析和注释&#xff08;四&#xff09;--- 跳跃表(skiplist) Redis…

Android Activity生命周期

Android生命周期 Android的生命周期&#xff1a;onCreate() -> onStart() -> onResume() -> onPause() -> onStop() -> onDestroy() 如下图所示&#xff1a; 1.当activity启动时系统会先调用onCreate(),然后调用onStart(),最后调用**onResume()**方法&#xff0…

date数据存入mysql_Date对象存入mysql数据库

java.sql.Date,java.sql.Time和java.sql.Timestamp三个都是java.util.Date的子类(包装类)。java.sql.Date是java.util.Date的子类&#xff0c;是一个包装了毫秒值的瘦包装器&#xff0c;允许 JDBC 将毫秒值标识为 SQL DATE 值。毫秒值表示自 1970 年 1 月 1 日 00:00:00 GMT 以…

盛严谨,严谨,再严谨。_评估员工调查的统计严谨性

盛严谨,严谨,再严谨。The human resources industry relies heavily on a wide range of assessments to support its functions. In fact, to ensure unbiased and fair hiring practices the US department of labor maintains a set of guidelines (Uniform Guidelines) to …

复权就是对股价和成交量进行权息修

* 所谓复权就是对股价和成交量进行权息修复,按照股票的实际涨跌绘制股价走势图, * 并把成交量调整为相同的股本口径。股票除权、除息之后&#xff0c;股价随之产生了变化&#xff0c; * 但实际成本并没有变化。 * 如&#xff1a;原来20元的股票&#xff0c;十送十之…

前端校验和后端校验

问&#xff1a;到底是前端校验好还是后端校验好呢&#xff1f; 答&#xff1a;后端校验比前端校验更安全&#xff0c;更可靠&#xff0c;前端校验可以增加用户体验&#xff0c;一般来说&#xff0c;在前端校验的东西在后端也必须校验&#xff08;比如登陆用户名、密码&#xff…

[MySQL] INFORMATION_SCHEMA 数据库包含所有表的字段

sql注入后可以通过该数据库获取所有表的字段信息1. COLLATIONS表 提供有关每个字符集的排序规则的信息。 COLLATIONS表包含以下列&#xff1a;COLLATION_NAME 排序规则名称。 CHARACTER_SET_NAME 与排序规则关联的字符集的名称。 ID 排序规则ID。 IS_DEFAULT 排序规则是否为其字…