Java中Stream流相关介绍
什么是Stream?
Stream是JDK8 API的新成员,它允许以声明性方式处理数据集合
特点
- 代码简洁: 函数式编程写出的代码简洁且意图明确,使用stream接口让你从此告别for循环
- 多核友好: Java函数式编程使得编写并行程序从未如此简单,你需要的全部就是调用一下方法
为什么要使用Stream API?
实际开发中,项目中多数数据源都来自于MySQL,Oracle等.但现在数据源可以更多了,有MongoDB,Redis等,而这些NoSQL的数据就需要Java层面去处理
Stream和Collection集合的区别:Collection是一种静态的内存数据结构,而Stream是有关计算的.前者是主要面向内存,存储在内存中,后者主要是面向CPU,通过CPU实现计算
注意:
- Stream自己不会存储元素
- Stream不会改变源对象.相反,他们会返回一个持有结果的新Stream
- Stream操作是延迟执行的.这意味着他们会等到需要结果的时候才执行
步骤
创建Stream
一个数据源(集合,数组等),获取一个流
中间操作
一个中间操作链,对数据源的数据进行处理
终止操作
一旦执行终止操作,就执行中间操作链,并产生结果.之后,不会再被使用
Stream的实例化
/**
* @PROJECT_NAME: myTest
* @DESCRIPTION: Stream的方法测试
* @USER: 罗龙达
* @DATE: 2021/2/21 2:33
*/
public class streamTest {
@Test
public void createStream(){
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jk");
//1. 通过集合创建stream -- 返回一个顺序流
Stream<String> listStream = strings.stream();
//返回一个并行流
Stream<String> paraStream = strings.parallelStream();
//2. 通过数组创建stream
IntStream arrStream = Arrays.stream(new int[]{1, 3, 45, 6, 877, 12});
//3. 通过Stream的of()方法
Stream<Integer> integerStream = Stream.of(1, 3, 5, 7, 9);
//4. 创建无限流
Stream.iterate(0,t -> t+2).limit(10).forEach(System.out::println);
//5. 生成
Stream.generate(Math::random).limit(10).forEach(System.out::println);
}
}
中间操作的部分API测试
@Test
public void InOperationTest() {
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "abc", "jk");
//1. 通过集合创建stream -- 返回一个顺序流
Stream<String> listStream = strings.stream();
System.out.println("-----------测试filter--------------");
/**
* filter(Predicate predicate) -- 接收lambda,从流中排除某些元素
* 查询列表中不为null的元素
*/
//
listStream.filter(s -> !s.equals("")).forEach(System.out::println);
System.out.println("--------------测试limit------------");
/**
*limit(n) -- 截断流,使元素不超过给定数量
*/
strings.stream().limit(3).forEach(System.out::println);
System.out.println("-------------测试skip--------------");
/**
*skip(n) -- 跳过元素,返回一个扔掉了前n个元素的流,如果流中元素不足n个,则返回一个空流
*/
strings.stream().skip(3).forEach(System.out::println);
System.out.println("------------测试distinct-----------");
/**
*distinct() -- 筛选,通过流所产生元素的hashCode()和equals()去除重复元素
*/
strings.stream().distinct().forEach(System.out::println);
}
Map映射API的部分测试
@Test
public void mapTest(){
System.out.println("----------------将字符串转换成大写---------------");
List<String> strings = Arrays.asList("aaa", "bb", "cccc", "d","eeeee","ff");
/**
* 接受一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素上,并将其映射成一个新的元素
*/
strings.stream().map(String::toUpperCase).forEach(System.out::println);
System.out.println("------------字符串长度大于3的元素的大写-----------");
/**
* 返回字符串长度大于3的元素的大写
*/
strings.stream().filter(s -> s.length() >3).map(String::toUpperCase).forEach(System.out::println);
/**
* flatMap(Function f) -- 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
*/
strings.stream().flatMap(streamTest::fromStringToStream).forEach(System.out::println);
}
//将字符串中的多个字符构成的集合转换为对应的流
public static Stream<Character> fromStringToStream(String s){
ArrayList<Character> list = new ArrayList<>();
for (char c : s.toCharArray()) {
list.add(c);
}
return list.stream();
}
map与flatMap的区别
- 从上面例子可以看出,map 生成的是个 1:1 映射,每个输入元素,都按照规则转换成为另外一个元素。还有一些场景,是一对多映射关系的,这时需要 flatMap。
- flatMap 把 inpuStream 中的层级结构扁平化,就是将最底层元素抽出来放到一起,最终 output 的新 Stream 里面已经没有 List 了,都是直接的数字。
排序API测试
@Test
public void sortTest() {
List<Integer> list = Arrays.asList(23, 45, -12, 2, 7, 89, 5);
System.out.println("----------从小到大排序测试---------");
list.stream().sorted().forEach(System.out::println);
System.out.println("----------从大到小排序测试---------");
list.stream().sorted((i1, i2) -> i2 - i1).forEach(System.out::println);
}
终止操作的部分API测试
@Test
public void termOperationTest(){
List<Integer> list = Arrays.asList(23, 45, -12, 2, 7, 89, 5);
System.out.println("-----------allMatchTest-------------");
/**
* allMatch(Predicate predicate) -- 检查是否匹配所有元素
*/
boolean allMatch = list.stream().allMatch(integer -> integer > 0);
System.out.println(allMatch);
System.out.println("-----------anyMatchTest-------------");
/**
* allMatch(Predicate predicate) -- 检查是否匹配所有元素
*/
boolean anyMatch = list.stream().anyMatch(integer -> integer > 0);
System.out.println(anyMatch);
System.out.println("----------noneMatchTest-------------");
/**
* noneMatch(Predicate predicate) -- 检查是否没有匹配的元素
*/
boolean noneMatch = list.stream().noneMatch(integer -> integer > 0);
System.out.println(noneMatch);
System.out.println("----------findFirstTest-------------");
/**
* findFirst() -- 返回流中第一个元素
*/
Optional<Integer> first = list.stream().findFirst();
System.out.println(first);
System.out.println("------------findAnyTest-------------");
/**
* findAny() -- 返回流中任一元素
*/
Optional<Integer> any = list.parallelStream().findAny();
System.out.println(any);
System.out.println("--------------countTest-------------");
/**
* count() -- 返回流中元素的总个数
*/
long count = list.stream().filter(i -> i > 0).count();
System.out.println(count);
System.out.println("------------max / min Test----------");
/**
* max/min(Comparator comparator) -- 返回流中最大值 / 最小值
*/
Optional<Integer> max = list.stream().max((i1, i2) -> i1 - i2);
System.out.println("max = " + max);
Optional<Integer> min = list.stream().min((i1, i2) -> i1 - i2);
System.out.println("min = " + min);
System.out.println("--------------forEachTest------------");
/**
* forEach(Consumer c) -- 内部迭代
*/
// list.stream().forEach(System.out::println);
list.forEach(System.out::println);
}
规约操作部分API测试
@Test
public void reduceTest(){
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
/**
* reduce(T identity, BinaryOperator) -- 可以将流中元素反复结合起来,得到一个值
*/
Integer reduce = list.stream().reduce(0, Integer::sum);
System.out.println("reduce = " + reduce);
/**
* reduce(BinaryOperator) -- 可以将流中元素反复结合起来,得到一个值,返回optional
*/
Optional<Integer> reduce2 = list.stream().reduce(Integer::sum);
Optional<Integer> reduce3 = list.stream().reduce((i1, i2) -> i1 + i2);
System.out.println("reduce2 = " + reduce2);
System.out.println("reduce3 = " + reduce3);
}
collect部分API测试
@Test
public void collectTest(){
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
/**
*collect(Collector c) 将流转换为其他形式.接受一个Collector接口的实现
* 查找大于4的数字,结果返回一个list / set
*/
List<Integer> collectList = list.stream().filter(i -> i > 4).collect(Collectors.toList());
collectList.forEach(System.out::println);
Set<Integer> collectSet = list.stream().filter(i -> i > 4).collect(Collectors.toSet());
collectSet.forEach(System.out::println);
}
Optional类
到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因.
以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,鼓励程序员写更干净的代码.受到Google Guava的启发,Optional类已经成为Java 8 类库的一部分
Optional类是一个容器类,他可以保存类型T的值,代表这个值存在,或者仅仅保存null,表示这个值不存在.原来用null表示一个值不存在,现在Optional可以更好地表达这个概念.并且可以避免空指针异常
Optional类的Javadoc描述如下: 这是一个可以为null的容器对象.如果值存在在isPresent()方法会返回true,调用get()方法会返回该对象
相关方法
# 1 创建Optional类对象的方法
- Optional.of(T t): 创建一个Optional实例,`t必须非空`
- Optional.empty(): 创建一个空的Optional实例
- Optional.ofNullable(T t): t可以为null
# 2 判断Optional容器中是否包含对象:
- boolean isPresent(): 判断是否包含对象
- void ifPresent(Consumer consumer): 如果有值,就执行Consumer接口的实现代码,并且该值会作为参数传给它
# 3 获取Optional容器的对象
- T get(): 如果调用对象包含值,返回该值,否则抛异常
- T orElse(): 如果有值则将其返回,否则返回指定的other对象
- T orElseGet(Supplier other): 如果有值将其返回,否则返回由Supplier接口实现提供的对象
- T orElseThrow(Supplier exceptionSupplier): 如果有值则将其返回,否则抛出由Supplier接口实现提供的异常
相关方法代码测试
- Girl实体类
public class Girl {
public Girl() {
}
public Girl(String name) {
this.name = name;
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
- Boy实体类
public class Boy {
private Girl girlFriend;
private String name;
public Boy() {
}
public Boy(Girl girlFriend, String name) {
this.girlFriend = girlFriend;
this.name = name;
}
public Girl getGirlFriend() {
return girlFriend;
}
public void setGirlFriend(Girl girlFriend) {
this.girlFriend = girlFriend;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Test
public void OptionalTest(){
Girl girl = new Girl();
girl=null;
Optional<Girl> optionalGirl = Optional.ofNullable(girl);
System.out.println("optionalGirl = " + optionalGirl);
}
//原始的getName方法
public String getGirlName(Boy boy){
return boy.getGirlFriend().getName();
}
//优化后的getName方法
public String getGirlNameAfterOptimizing(Boy boy){
Optional<Boy> boyOptional = Optional.ofNullable(boy);
//此时boy1一定非空
Boy boy1 = boyOptional.orElse(new Boy(new Girl("杨幂"), "我"));
Girl girlFriend = boy1.getGirlFriend();
Optional<Girl> girlFriend1 = Optional.ofNullable(girlFriend);
//girlOption一定非空
Girl girlOptional = girlFriend1.orElse(new Girl("赵丽颖"));
return "女朋友的名字 : " + girlOptional.getName();
}
@Test
public void testGetName(){
Boy boy = new Boy();
// boy = null;
boy.setGirlFriend(null);
// boy.setGirlFriend(new Girl("迪丽热巴"));
System.out.println(getGirlNameAfterOptimizing(boy));
}
Java中Stream流相关介绍的更多相关文章
- java中IO流相关知识点
(一) 下边使用outputStream字节输出流进行写操作 package zdbIO;import java.io.File;import java.io.FileNotFoundExceptio ...
- java中数据流的简单介绍
java中的I/O操作主要是基于数据流进行操作的,数据流表示了字符或者字节的流动序列. java.io是数据流操作的主要软件包 java.nio是对块传输进行的支持 数据流基本概念 “流是磁盘或其它外 ...
- 理解Java中字符流与字节流的区别
1. 什么是流 Java中的流是对字节序列的抽象,我们可以想象有一个水管,只不过现在流动在水管中的不再是水,而是字节序列.和水流一样,Java中的流也具有一个“流动的方向”,通常可以从中读入一个字节序 ...
- 关于java中Stream理解
关于java中Stream理解 Stream是什么 Stream:Java 8新增的接口,Stream可以认为是一个高级版本的Iterator.它代表着数据流,流中的数据元素的数量可以是有限的, 也可 ...
- 理解Java中字符流与字节流
1. 什么是流 Java中的流是对字节序列的抽象,我们可以想象有一个水管,只不过现在流动在水管中的不再是水,而是字节序列.和水流一样,Java中的流也具有一个"流动的方向",通常可 ...
- 理解Java中字符流与字节流的区别(转)
1. 什么是流 Java中的流是对字节序列的抽象,我们可以想象有一个水管,只不过现在流动在水管中的不再是水,而是字节序列.和水流一样,Java中的流也具有一个“流动的方向”,通常可以从中读入一个字节序 ...
- Java中的流(1)流简介
简介 1.在java中stream代表一种数据流(源),java.io的底层数据元.(比作成水管)2.InputStream 比作进水管,水从里面流向你,你要接收,read3.OutputStream ...
- Java中Date各种相关用法
Java中Date各种相关用法(一) 1.计算某一月份的最大天数 Java代码 Calendar time=Calendar.getInstance(); time.clear(); time.set ...
- Java中IO流的总结
有关Java中IO流总结图 流分类 按方向分 输入流 输出流 按单位分 字节流 字符流 按功能分 节点流 处理流(过滤流) 其他 所有的流继承与这四类流:InputSteam.OutputStream ...
随机推荐
- Git本地操作2
code[class*="language-"], pre[class*="language-"] { color: rgba(51, 51, 51, 1); ...
- 前瞻|Amundsen的数据血缘功能
目前,Amundsen并不支持表级别和列级别的数据血缘功能,也没有办法展示数据的来龙去脉. 作为Amundsen一项非常核心的功能,Lineage功能早已经提上日程,并进入设计与研发阶段.本位将展示此 ...
- Ubuntu20.04linux内核(5.4.0版本)编译准备与实现过程-编译前准备(1)
最近项目也和linux kernel技术有关,调试内核和内核模块.修改内核源码,是学习内核的重要技术手段之一.应用这些技术时,都有一本基本的要求,那就是编译内核.因此,在分析内核调试技术之前,本随笔给 ...
- 【Makefile】2-Makefile的介绍及原理
目录 前言 概念 Chapter 2:介绍 2.1 makefile的规则 2.3 make 是如何工作的 ** 2.5 让 make 自动推导 2.8 Makefile 里面有什么 2.9 Make ...
- 力扣 - 560. 和为K的子数组
目录 题目 思路1(前缀和) 代码 复杂度分析 思路2(前缀和+哈希表优化) 代码 复杂度分析 题目 560. 和为K的子数组 思路1(前缀和) 构建前缀和数组,可以快速计算任意区间的和 注意:计算区 ...
- kubernetes删除pod一直处于terminating状态的解决方法
kubernetes删除pod一直处理 Terminating状态 # kubectl get po -n mon NAME READY STATUS RESTARTS AGE alertmanage ...
- Elasticsearch集群升级指引
目录 背景 第一部分 版本升级指引 第二部分 升级方法和具体步骤 总结 参考文献及资料 背景 Elasticsearch集群的版本升级是一项重要的集群维护工作.本篇文章参考官方文档,将详细介绍相关细节 ...
- [.net] 关于Exception的几点思考和在项目中的使用(二)
本文链接: https://www.cnblogs.com/hubaijia/p/about-exceptions-2.html 系列文章: 关于Exception的几点思考和在项目中的使用(一) 关 ...
- 【DB宝47】企业知识分享+团队协作神器之Confluence
目录 一.Confluence简介 二.知识库软件对比 三.快速安装confluence 7.4.6版本 四.confluence基本操作简介 4.1.创建空间(Space) 4.2.配置空间权限 4 ...
- 蒙特卡洛——使用CDF反函数生成非均匀随机数
均匀随机数生成 先来说说均匀随机数生成,这是非均匀随机数的生成基础. 例如,我们现在有drand()函数,可以随机生成[0,1]范围内的均匀随机数. 要求一个drand2()函数,能够生成[0 ...