快速掌握Java8 Stream函数式编程技巧
函数式编程优势
- “函数第一位”,即函数可以出现在任何地方。
- 可以把函数作为参数传递给另一个函数,还可以将函数作为返回值。
- 让代码的逻辑更清晰更优雅。
- 减少了可变量(Immutable Variable)的声明,程序更为安全。
- 支持惰性计算。
Lambda语法三部分
- 一个括号内用逗号分隔的形式参数,参数是函数式接口里面方法的参数
- 一个箭头符号:->
- 方法体,可以是表达式和代码块,方法体函数式接口里面方法的实现,如果是代码块,则必须用{}来包裹起来,且需要一个return 返回值,但有个例外,若函数式接口里面方法返回值是void,则无需{}。例如:
- (parameters) -> expression 或者 (parameters) -> { statements; }
方法引用是lambda表达式的一个简化写法,所引用的方法其实是lambda表达式的方法体实现,语法也很简单,左边是容器(可以是类名,实例名),中间是“::”,右边是相应的方法名。如下所示:
ObjectReference::methodName
一般方法的引用格式是:
- 如果是静态方法,则是ClassName::methodName。如 Object ::equals
- 如果是实例方法,则是Instance::methodName。如Object obj=new Object();obj::equals;
- 构造函数.则是ClassName::new
Stream是什么
Stream是Java8中新加入的api,更准确的说: Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作,或者大批量数据操作 。Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。
以前我们处理复杂的数据只能通过各种for循环,不仅不美观,而且时间长了以后可能自己都看不太明白以前的代码了,但有Stream以后,通过filter,map,limit等等方法就可以使代码更加简洁并且更加语义化。 Stream的效果就像上图展示的它可以先把数据变成符合要求的样子(map),吃掉不需要的东西(filter)然后得到需要的东西(collect)。
Stream操作分类
Stream上的所有操作分为两类:中间操作和结束操作,中间操作只是一种标记,只有结束操作才会触发实际计算。中间操作又可以分为无状态的(Stateless)和有状态的(Stateful),无状态中间操作是指元素的处理不受前面元素的影响,而有状态的中间操作必须等到所有元素处理之后才知道最终结果,比如排序是有状态操作,在读取所有元素之前并不能确定排序结果;结束操作又可以分为短路操作和非短路操作,短路操作是指不用处理全部元素就可以返回结果,比如找到第一个满足条件的元素。之所以要进行如此精细的划分,是因为底层对每一种情况的处理方式不同。
Stream API等价实现
求出字符串集合中所有以字母A开头字符串的最大长度
int longest = 0;
for(String str : strings){
if(str.startsWith("A")){// 1. filter(), 保留以A开头的字符串
int len = str.length();// 2. mapToInt(), 转换成长度
longest = Math.max(len, longest);// 3. max(), 保留最长的长度
}
}
int longest = strings.stream()
.filter(str -> str.startsWith("A"))
.mapToInt(str -> str.length())
//.mapToInt(String::length)
.max();
Stream串行与并行
Stream可以分为串行与并行两种,串行流和并行流差别就是单线程和多线程的执行。 default Stream stream() : 返回串行流 default Stream parallelStream() : 返回并行流 stream()和parallelStream()方法返回的都是java.util.stream.Stream<E>类型的对象,说明它们在功能的使用上是没差别的。唯一的差别就是单线程和多线程的执行。
Stream性能总结
1.对于简单操作,比如最简单的遍历,Stream串行API性能明显差于显示迭代,但并行的Stream API能够发挥多核特性。
2.对于复杂操作,Stream串行API性能可以和手动实现的效果匹敌,在并行执行时Stream API效果远超手动实现。
所以,如果出于性能考虑:
- 对于简单操作推荐使用外部迭代手动实现。
- 对于复杂操作,推荐使用Stream API。
- 在多核情况下,推荐使用并行Stream API来发挥多核优势。
- 单核情况下不建议使用并行Stream API。 如果出于代码简洁性考虑,使用Stream API能够写出更短的代码。即使是从性能方面说,尽可能的使用Stream API也另外一个优势,那就是只要Java Stream类库做了升级优化,代码不用做任何修改就能享受到升级带来的好处。
Stream 来源
所有流计算都有一种共同的结构:它们具有一个流来源、0 或多个中间操作,以及一个终止操作。流的元素可以是对象引用 (Stream<String>),也可以是原始整数 (IntStream)、长整型 (LongStream) 或双精度 (DoubleStream)。
因为 Java 程序使用的大部分数据都已存储在集合中,所以许多流计算使用集合作为它们的来源。JDK 中的 Collection 实现都已增强,可充当高效的流来源。但是,还存在其他可能的流来源,比如数组、生成器函数或内置的工厂(比如数字范围),而且可以编写自定义的流适配器,以便可以将任意数据源充当流来源。如上图中一些流生成方法。
Stream 操作
中间操作负责将一个流转换为另一个流,中间操作包括 filter()(选择与条件匹配的元素)、map()(根据函数来转换元素)、distinct()(删除重复)、limit()(在特定大小处截断流)和 sorted()。一些操作(比如 mapToInt())获取一种类型的流并返回一种不同类型的流。
中间操作始终是惰性的:调用中间操作只会设置流管道的下一个阶段,不会启动任何操作。重建操作可进一步划分为无状态 和有状态 操作。无状态操作(比如 filter() 或 map())可独立处理每个元素,而有状态操作(比如 sorted() 或 distinct())可合并以前看到的影响其他元素处理的元素状态。
数据集的处理在执行终止操作时开始,比如缩减(sum() 或 max())、应用 (forEach()) 或搜索 (findFirst()) 操作。终止操作会生成一个结果或副作用。执行终止操作时,会终止流管道,如果您想再次遍历同一个数据集,可以设置一个新的流管道。如下给出了一些终止流操作。
Stream 流与集合比较
集合是一种数据结构,它的主要关注点是在内存中组织数据,而且集合会在一段时间内持久存在。 流的关注点是计算,而不是数据。流没有为它们处理的元素提供存储空间,而且流的生命周期更像一个时间点 — 调用终止操作。 不同于集合,流也可以是无限的(Stream.generate、Stream.iterate);相应地,一些操作(limit()、findFirst())是短路,而且可在无限流上运行有限的计算。 程序 = 数据结构 + 算法,集合即数据结构,流操作相当于算法。
数据形式:集合是一个内存中的数据结构,它包含数据结构中目前所有的值——集合中的每个元素都得先算出来才能添加到集合中。(你可以往集合里加东西或者删东西,但是不管什么时候,集合中的每个元素都是放在内存里的,元素都得先算出来才能成为集合的一部分。) 相比之下,流则是在概念上固定的数据结构(你不能添加或删除元素),其元素则是按需计算的。
迭代方式:使用Collection接口需要用户去做迭代(比如用for-each),这称为外部迭代。相反,Streams库使用内部迭代——它帮你把迭代做了,还把得到的流值存在了某个地方,你只要给出一个函数说要干什么就可以了。Steams库的内部迭代可以自动选择一种适合你硬件的数据表示和并行实现。
Stream 基本使用
filter筛选(中间操作)
输出:4,5
List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5); Stream<Integer> stream = integerList.stream().filter(i -> i > 3);
distinct去重(中间操作)
输出:1,2,3,4,5
List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5); Stream<Integer> stream = integerList.stream().distinct();
limit限制(中间操作)
输出:1,1,2
List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5); Stream<Integer> stream = integerList.stream().limit(3);
skip跳过(中间操作)
输出:2,3,4,5
List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5); Stream<Integer> stream = integerList.stream().skip(2);
map流映射(中间操作)
输出:6, 7, 2, 6
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action"); Stream<Integer> stream = stringList.stream().map(String::length);
flatMap流转换(中间操作)
输出:1, 2, 3, 4
将一个流中的每个值都转换为另一个流
List<List<Integer>> lists = new ArrayList<List<Integer>>() {{
add(Arrays.asList(1, 2));
add(Arrays.asList(3, 4));
}}; Stream<Integer> stream = lists.stream().flatMap(List::stream); //Stream<Integer> stream = lists.stream().flatMap(list -> list.stream());
allMatch匹配所有(中间操作)
输出:
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); if (integerList.stream().allMatch(i -> i > 3)) {
System.out.println("值都大于3");
}
noneMatch全部不匹配(中间操作)
输出:
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); if (integerList.stream().noneMatch(i -> i > 3)) {
System.out.println("值都小于3");
}
anyMatch匹配其中一个(中间操作)
输出:存在大于3的值
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
if (integerList.stream().anyMatch(i -> i > 3)) {
System.out.println("存在大于3的值");
}
findFirst查找第一个(终端操作)
输出:4
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> result = integerList.stream().filter(i -> i > 3).findFirst();
findAny随机查找一个(终端操作)
输出:存在大于3的值
和findFirst操作相比,并行流优势更大
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
if (integerList.stream().findAny(i -> i > 3)) {
System.out.println("存在大于3的值");
}
Stream 常用统计
List<Integer> ints = Arrays.asList(1, 1, 2, 2, 3);
//统计流中元素个数
ints.stream().count();
ints.stream().collect(Collectors.counting()); //获取流中最小值
ints.stream().min(Integer::compareTo);
ints.stream().collect(Collectors.minBy(Integer::compareTo)); //获取流中最大值
ints.stream().max(Integer::compareTo);
ints.stream().collect(Collectors.maxBy(Integer::compareTo)); //求和
ints.stream().mapToInt(Integer::intValue).sum();
ints.stream().collect(Collectors.summingInt(Integer::intValue));
ints.stream().reduce(0, Integer::sum); //平均值
ints.stream().collect(Collectors.averagingInt(Integer::intValue)); //通过summarizingInt同时求总和、平均值、最大值、最小值
ints.stream().collect(Collectors.summarizingInt(Integer::intValue));
Stream 终端操作(collect)
List<Integer> ints = Arrays.asList(1, 1, 2, 2, 3);
//返回List
ints.stream().collect(Collectors.toList()); //返回Set
ints.stream().collect(Collectors.toSet()); //返回Map
ints.stream().collect(Collectors.toMap(k -> k, v -> v, (v1, v2) -> v1)); //group分组
ints.stream().collect(Collectors.groupingBy(k -> k)); //partitioningBy分区
ints.stream().collect(Collectors.partitioningBy(k -> k % 2 == 0));
Stream参考文献
快速掌握Java8 Stream函数式编程技巧的更多相关文章
- 谈一谈Java8的函数式编程(二) --Java8中的流
流与集合 众所周知,日常开发与操作中涉及到集合的操作相当频繁,而java中对于集合的操作又是相当麻烦.这里你可能就有疑问了,我感觉平常开发的时候操作集合时不麻烦呀?那下面我们从一个例子说起. 计 ...
- Java Stream函数式编程案例图文详解
导读 作者计划把Java Stream写成一个系列的文章,本文只是其中一节.更多内容期待您关注我的号! 一.什么是Java Stream? Java Stream函数式编程接口最初是在Java 8中引 ...
- Java Stream函数式编程图文详解(二):管道数据处理
一.Java Stream管道数据处理操作 在本号之前发布的文章<Java Stream函数式编程?用过都说好,案例图文详解送给你>中,笔者对Java Stream的介绍以及简单的使用方法 ...
- 谈一谈Java8的函数式编程 (三) --几道关于流的练习题
为什么要有练习题? 所谓学而不思则罔,思而不学则殆,在系列第一篇就表明我认为写博客,既是分享,也是自己的巩固,我深信"纸上得来终觉浅,绝知此事要躬行"的道理,因此之后的几篇博 ...
- Java8自定义函数式编程接口和便捷的引用类的构造器及方法
什么是函数编程接口? 约束:抽象方法有且只有一个,即不能有多个抽象方法,在接口中覆写Object类中的public方法(如equal),不算是函数式接口的方法. 被@FunctionalInterfa ...
- java8 lambda 函数式编程
package com.atguigu.java8; import java.util.ArrayList; import java.util.Comparator; import java.util ...
- Java Stream函数式编程第三篇:管道流结果处理
一.Java Stream管道数据处理操作 在本号之前写过的文章中,曾经给大家介绍过 Java Stream管道流是用于简化集合类元素处理的java API.在使用的过程中分为三个阶段.在开始本文之前 ...
- 3分钟看完Java 8——史上最强Java 8新特性总结之第三篇 函数式编程技巧
目录 · 改写设计模式 · 策略模式(Strategy Pattern) · 模板方法模式(Template Method Pattern) · 观察者模式(Observer Pattern) · 责 ...
- Java8函数式编程探秘
引子 将行为作为数据传递 怎样在一行代码里同时计算一个列表的和.最大值.最小值.平均值.元素个数.奇偶分组.指数.排序呢? 答案是思维反转!将行为作为数据传递. 文艺青年的代码如下所示: public ...
随机推荐
- 《Offer一箩筐》一份高质量「简历」撰写指南,望打扰!!
「MoreThanJava」 宣扬的是 「学习,不止 CODE」. 如果觉得 「不错」 的朋友,欢迎 「关注 + 留言 + 分享」,文末有完整的获取链接,您的支持是我前进的最大的动力! Hi~ 这里是 ...
- css常用属性之绝对定位、相对定位、滚动条属性、背景图属性、字体、鼠标、超链接跳转页面
1.绝对定位position: fixed(比如广告页面向下滑动的时候,页面最上方有个标题不能随之滑动,就需要用到position: fixed,同时还需要用到一个标签(标签高度很高才会出现滚动的情况 ...
- [程序员代码面试指南]二叉树问题-找到二叉树中的最大搜索二叉树(树形dp)
题意 给定一颗二叉树的头节点,已知所有节点的值都不一样,找到含有节点最多的搜索二叉子树,并返回这个树的头节点. 题解 在后序遍历过程中实现. 求解步骤按树形dp中所列步骤.可能性三种:左子树最大.右子 ...
- python 中简单的输出语句
1 python 中简单的输出语句 #coding:utf-8#输出的是整数,得到的也是整数,用raw_inputusername=raw_input('请输入用户名:')#输出的是整数或者字符串,得 ...
- 11.深入k8s:kubelet工作原理及源码分析
转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com 源码版本是1.19 kubelet信息量是很大的,通过我这一篇文章肯定是讲不全的,大家可 ...
- Jakartase_IO流_ — Commons IO_(IO流终极篇)
一.前言 Apache Commons IO是Apache基金会创建并维护的Java函数库. 它提供了许多类使得开发者的常见任务变得简单,同时减少重复代码 二.Commons IO 类库 2.1 Fi ...
- Flutter学习二之Dart语言介绍
上次我记录了Flutter的环境搭建,这次来简单记录一下Drat语言,Flutter是 Google推出并开源的移动应用开发框架,开发语言是Dart,那么Dart语言和其他的语言在语法上有上面区别呢, ...
- vue学习06 v-show指令
目录 vue学习06 v-show指令 v-show指令是:根据真假切换元素的显示状态 原理是修改元素的display,实现显示隐藏 指令后面的内容,最终都会解析为布尔值(true和false) 练习 ...
- Python爬取股票信息,并实现可视化数据
前言 截止2019年年底我国股票投资者数量为15975.24万户, 如此多的股民热衷于炒股,首先抛开炒股技术不说, 那么多股票数据是不是非常难找, 找到之后是不是看着密密麻麻的数据是不是头都大了? 今 ...
- 微服务电商项目发布重大更新,打造Spring Cloud最佳实践!
Spring Cloud实战电商项目mall-swarm地址:转发+关注 私信我获取地址 系统架构图 系统架构图 项目组织结构 mall├── mall-common-- 工具类及通用代码模块├─ ...