1.简介

Stream流 最全的用法
Stream 能用来干什么?用来处理集合,通过 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询,Stream API 提供了一种高效且易于使用的处理数据的方式

为什么用Java 8 Stream ?因为 操作简单
为什么操作简单?因为 Lambda 表达式,它极大的提高了编程效率和程序可读性
怎么操作流? 首先你的有个数据源(数组、集合),操作会产生新的流对象,原来的流对象不会改变
流用法有结束操作,这种代码不是你写了一个方法就执行一个方法,而是最后触发结束操作的时候才统一执行的,collect、foreach 方法就是一种结束方法,详情看代码及结果参考 2.映射map、flatMap用法 部分

2.具体用法

2.1 创建流

 // 集合创建流
List<String> list = new ArrayList<>();
// 获取一个顺序流
Stream<String> listStream = list.stream();
// 获取一个并行流
Stream<String> parallelListStream = list.parallelStream(); // 数组创建流
Integer[] nums = new Integer[] { 1, 2, 3, 4, 5 };
Stream<Integer> arrStream = Arrays.stream(nums);
arrStream.forEach(System.out::println);// 1 2 3 4 5 // 静态方法of创建流
Stream<Integer> ofStream = Stream.of(1, 2, 3, 4, 5);
ofStream.forEach(System.out::println);// 1 2 3 4 5 // 静态方法iterate 创建流
Stream<Integer> iterateStream = Stream.iterate(1, (x) -> x + 10).limit(4);
iterateStream.forEach(System.out::println); // 1 11 21 31 // 静态方法generate 创建流
Stream<Double> generateStream = Stream.generate(Math::random).limit(2);
generateStream.forEach(System.out::println);

  

2.2 操作流

1.过滤

filter:过滤流中的某些元素(可以做一些基本的判空、替换、判断逻辑操作)
limit(n):获取n个元素,结果获取几个元素
skip(n):跳过n元素,配合limit(n)可实现分页
distinct:通过流中元素的 hashCode() 和 equals() 去除重复元素

     //filter 判空
Stream<Integer> notNullStreamObj = Stream.of(1, 2, null, 4, 5, 6, 7, null, 2);
Stream<Integer> notNullStream = notNullStreamObj.filter(i -> (null != i));
notNullStream.forEach(System.out::println);//1 2 4 5 6 7 2 //filter 逻辑判断
Stream<Integer> logicStreamObj = Stream.of(1, 2, null, 4, 5, 6, 7, null, 2);
Stream<Integer> logicStream = logicStreamObj.filter(i -> (i != null && i > 5));
logicStream.forEach(System.out::println); // 6 7 //filter 替换
Stream<String> strStreamObj = Stream.of("aa", "ab", null, "ac", "bd", "ee");
Stream<String> strStream = strStreamObj.filter(str -> (null != str && str.contains("a")));
strStream.forEach(System.out::println); // aa ab ac //skip 跳过
Stream<String> skipStreamObj = Stream.of("aa", "ab", null, "ac", "bd", "ee");
Stream<String> skipStream = skipStreamObj.skip(2);
skipStream.forEach(System.out::println); // null ac bd ee //distinct 去重
Stream<String> disStreamObj = Stream.of("aa", "ab", null, "ac", "aa", "ab", null, "ee");
Stream<String> disStream = disStreamObj.distinct();
disStream.forEach(System.out::println); // aa ab null ac ee

  

2.映射

map:接收一个函数作为参数,该函数会被应用到每个元素上,映射成一个新的元素。
flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
peek:这个操作很骚,类似map只不过map 是Func函数,提供返回值,而peer是取出元素,Consumer表达式设值,我个人觉得没啥区别呢,官方文档提示:该方法主要用于调试,做一些消耗这个对象但不修改它的东西,没啥事不要用
很想问一下 这俩map、flatMap 区别 ,细品,你细品,你细细品
map是将每个元素 映射成一个新元素,除非你过滤了,否则不会改变元素个数
flatMap是将原流中的每个值都变成另一个流,然后把流合并串起来,必须有返回值,拼装成新的流

     //map 把包含a的元素,替换成| 注意,注意, 元素还是一个整体,对每个元素
Stream<String> mapStreamObj = Stream.of("a,b,c", "a,e,f", "g,h,i");
Stream<String> mapStream = mapStreamObj.map(str -> str.replaceAll(",", "|"));
mapStream.forEach(System.out::println); // a|b|c a|e|f h|i|j //flatMap 可以把元素 切分后,再按照新元素组成新的字符串
Stream<String> flatMapStreamObj = Stream.of("a,b,c", "a,e,f", "g,h,i");
Stream<String> flatMapStream = flatMapStreamObj.flatMap(str -> {
String[] arr = str.split(",");
Stream<String> result = Arrays.stream(arr);
return result;
});
flatMapStream.forEach(System.out::println); //a b c d e f g h i System.out.println("1===========");
Stream<String> peekStreamObj = Stream.of("a,b,c", "a,e,f", "g,h,i");
Stream<String> peekStream = peekStreamObj.peek(e -> System.out.println("Filtered value: " + e))
.map(String::toUpperCase)
.peek(e -> System.out.println("Mapped value: " + e));
System.out.println("2=========== peek代码结束,但是日志没打印");
Set<String> stringSet = peekStream.collect(Collectors.toSet());
System.out.println("3=========== collect结束操作,代码日志打印");
stringSet.forEach(System.out::println);

  

map执行结果

//看下执行结果,说明 collect才是结束操作,代码结束,但是并不是真正结束
1===========
2=========== peek代码结束,但是日志没打印
Filtered value: a,b,c
Mapped value: A,B,C
Filtered value: a,e,f
Mapped value: A,E,F
Filtered value: g,h,i
Mapped value: G,H,I
3=========== collect结束操作,代码日志打印
A,B,C
A,E,F
G,H,I

  

3.排序

sorted():自然排序,流中元素需实现Comparable接口
sorted(Comparator com):定制排序,自定义Comparator排序器
先构建一个User类

 public static class User {
private String name;
private Integer age; public User(String name, Integer age) {
this.name = name;
this.age = age;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Integer getAge() {
return age;
} public void setAge(Integer age) {
this.age = age;
} @Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

  

然后 看下sort用法

//按字母排序
Stream<String> sortStreamObj = Stream.of("a,e,f", "a,d,c", "a,b,i");
Stream<String> sortStream = sortStreamObj.sorted();
sortStream.forEach(System.out::println); //abi adc aef User u1 = new User("bb", 1);
User u2 = new User("aa", 2);
User u3 = new User("cc", 3);
User u4 = new User("aa", 4); Set<User> userSet = Sets.newHashSet(u1, u2, u3, u4);
Stream<User> userStream = userSet.stream().sorted(
(obj1, obj2) -> {
if (obj1.getName().equals(obj2.getName())) {
//name相等 按age
return obj1.getAge() - obj2.getAge();
}
return obj1.getName().compareTo(obj2.getName());
}
); userStream.forEach(System.out::println);// u2 u4 u1 u3

  

sort 执行结果

a,b,i
a,d,c
a,e,f
User{name='aa', age=2}
User{name='aa', age=4}
User{name='bb', age=1}
User{name='cc', age=3}

  

4.流匹配

allMatch:接收一个 Predicate 函数,当流中每个元素都符合该断言时才返回true,否则返回false
noneMatch:接收一个 Predicate 函数,当流中每个元素都不符合该断言时才返回true,否则返回false
anyMatch:接收一个 Predicate 函数,只要流中有一个元素满足该断言则返回true,否则返回false
findFirst:返回流中第一个元素
findAny:返回流中的任意元素
count:返回流中元素的总个数
max:返回流中元素最大值
min:返回流中元素最小值

        List<Integer> numLists = Arrays.asList(3, 4, 5, 6, 10);
// 全部匹配 - true
boolean allMatch1 = numLists.stream().allMatch(e -> e > 2); //true
System.out.println("allMatch1:" + allMatch1); // 全部匹配 - true
boolean allMatch2 = numLists.stream().allMatch(e -> e > 5); //false
System.out.println("allMatch2:" + allMatch2); // 全部都不符合 - true
boolean noneMatch = numLists.stream().noneMatch(e -> e > 20); //true
System.out.println("noneMatch:" + noneMatch); // 任一元素符合 - true
boolean anyMatch = numLists.stream().anyMatch(e -> e > 4); //true
System.out.println("anyMatch:" + anyMatch); //返回第一个
Integer findFirst = numLists.stream().findFirst().get(); //3
System.out.println("findFirst:" + findFirst); //返回任一个
Integer findAny = numLists.stream().findAny().get();
System.out.println("findAny:" + findAny); //返回 count
long count = numLists.stream().count(); //5
System.out.println("count:" + count); //返回max
Integer max = numLists.stream().max(Integer::compareTo).get(); //10
System.out.println("max:" + max); //返回min
Integer min = numLists.stream().min(Integer::compareTo).get();//3
System.out.println("min:" + min);

  

匹配执行结果

allMatch1:true
allMatch2:false
noneMatch:true
anyMatch:true
findFirst:3
findAny:3
count:5
max:10
min:3

  

5.组合操作

Reduce 就是组合操作
Reduce(BinaryOperator accumulator) 没有起始值,按照运算规则进行运算操作
解释:第一次执行时,accumulator函数的第一个参数为流中的第一个元素,第二个参数为流中元素的第二个元素,按照函数进行操作;
第二次执行时,第一个参数为第一次函数执行操作的结果,第二个参数为流中的第三个元素;往下依次类推,返回Optinal 通过get()方法获取结果
Reduce(T identity, BinaryOperator accumulator)含有初始值,第二个是第一个的变形,跟第一个方法对比,不同的是此次这个会接受一个identity参数,用来指定Stream循环的初始值。如果Stream为空,就直接返回该值,特殊:该方法不会返回 Optional

Optional sumResult = Stream.of(1, 2, 3, 4)
.reduce((sum, item) -> {
System.out.println("sum : " + sum);
sum += item;
System.out.println("item: " + item);
System.out.println("sum+ : " + sum);
System.out.println("-----——---");
return sum;
});
System.out.println("========sumResult: " + sumResult.get()); Integer sumDefineResult = Stream.of(1, 2, 3, 4)
.reduce(100, (sum, item) -> {
System.out.println("sum : " + sum);
sum += item;
System.out.println("item: " + item);
System.out.println("sum+ : " + sum);
System.out.println("---——-----");
return sum;
});
System.out.println("========sumDefineResult: " + sumDefineResult);

  

reduce 执行结果

//下面是执行结果
//查看执行结果
sum : 1
item: 2
sum+ : 3
-----——---
sum : 3
item: 3
sum+ : 6
-----——---
sum : 6
item: 4
sum+ : 10
-----——---
========sumResult: 10
sum : 100
item: 1
sum+ : 101
---——-----
sum : 101
item: 2
sum+ : 103
---——-----
sum : 103
item: 3
sum+ : 106
---——-----
sum : 106
item: 4
sum+ : 110
---——-----
========sumDefineResult: 110

  

6. 收集转换操作

这是个最最最最最基本的操作,10个流操作 9个都会使用到当前操作

collect(Collectors.toList()) 转换List
collect(Collectors.toSet()) 转换Set
Collectors.toMap(key, value) 转换Map ,如果key重复,!!!报错
Collectors.joining() join进行拼接
Collectors.groupingBy(key) 以Key为map的 key分组
Collectors.partitioningBy(规则) 以规则分区 比如 >5 ,map key为true,false

        User s1 = new User("aa", 1);
User s2 = new User("bb", 2);
User s3 = new User("cc", 3);
User s4 = new User("dd", 2);
List<User> list = Arrays.asList(s1, s2, s3, s4); //转换list
List<Integer> ageList = list.stream().map(User::getAge).collect(Collectors.toList()); // [1, 2, 3]
System.out.println(ageList.toString()); //转成set
Set<Integer> ageSet = list.stream().map(User::getAge).collect(Collectors.toSet()); // [1, 2, 3]
System.out.println(ageSet); //转成map,注:key不能相同,否则报错
Map<String, Integer> userMap = list.stream().collect(Collectors.toMap(User::getName, User::getAge)); // {cc=10, bb=20, aa=10}
System.out.println(userMap); //字符串分隔符连接
String joinName = list.stream().map(User::getName).collect(Collectors.joining(",", "(", ")")); // (aa,bb,cc)
System.out.println(joinName); //分组
Map<Integer, List<User>> ageMap = list.stream().collect(Collectors.groupingBy(User::getAge));
System.out.println(ageMap); //多重分组,先根据类型分再根据年龄分
Map<Integer, Map<Integer, List<User>>> typeAgeMap = list.stream().collect(Collectors.groupingBy(User::getAge, Collectors.groupingBy(User::getAge)));
System.out.println(typeAgeMap); //分区
//分成两部分,true 一部分age大于2岁, false 一部分age小于等于2岁
Map<Boolean, List<User>> partMap = list.stream().collect(Collectors.partitioningBy(v -> v.getAge() > 2));
System.out.println(partMap);

  

collect 执行结果

[1, 2, 3, 2]
[1, 2, 3]
{dd=2, cc=3, bb=2, aa=1}
(aa,bb,cc,dd)
{1=[User{name='aa', age=1}], 2=[User{name='bb', age=2}, User{name='dd', age=2}], 3=[User{name='cc', age=3}]}
{1={1=[User{name='aa', age=1}]}, 2={2=[User{name='bb', age=2}, User{name='dd', age=2}]}, 3={3=[User{name='cc', age=3}]}}
{false=[User{name='aa', age=1}, User{name='bb', age=2}, User{name='dd', age=2}], true=[User{name='cc', age=3}]}

  

最后

大家看完有什么不懂的可以在下方留言讨论,也可以关注我私信问我,我看到后都会回答的。也欢迎大家关注我的公众号:前程有光,金三银四跳槽面试季,整理了1000多道将近500多页pdf文档的Java面试题资料,文章都会在里面更新,整理的资料也会放在里面。谢谢你的观看,觉得文章对你有帮助的话记得关注我点个赞支持一下!

Java8用了这么久了,Stream 流用法及语法你都知道吗?的更多相关文章

  1. 深度分析:java8的新特性lambda和stream流,看完你学会了吗?

    1. lambda表达式 1.1 什么是lambda 以java为例,可以对一个java变量赋一个值,比如int a = 1,而对于一个方法,一块代码也是赋予给一个变量的,对于这块代码,或者说被赋给变 ...

  2. Java8新特性之方法引用&Stream流

    Java8新特性 方法引用 前言 什么是函数式接口 只包含一个抽象方法的接口,称为函数式接口. 可以通过 Lambda 表达式来创建该接口的对象.(若 Lambda 表达式抛出一个受检异常(即:非运行 ...

  3. JAVA8学习——从使用角度深入Stream流(学习过程)

    Stream 流 初识Stream流 简单认识一下Stream:Stream类中的官方介绍: /** * A sequence of elements supporting sequential an ...

  4. Java8学习(4)-Stream流

    Stream和Collection的区别是什么 流和集合的区别是什么? 粗略地说, 集合和流之间的差异就在于什么时候进行计算.集合是一个内存中的数据结构,它包含数据结构中目前所有的值--集合中的每个元 ...

  5. 这可能是史上最好的 Java8 新特性 Stream 流教程

    本文翻译自 https://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/ 作者: @Winterbe 欢迎关注个人微信公众 ...

  6. java8 Stream的实现原理 (从零开始实现一个stream流)

    1.Stream 流的介绍 1.1 java8 stream介绍 java8新增了stream流的特性,能够让用户以函数式的方式.更为简单的操纵集合等数据结构,并实现了用户无感知的并行计算. 1.2  ...

  7. Java8的Stream流(一) --- 基础用法

    Java8中的Stream Stream使用一种类似用SQL语句从数据库查询数据的直观方式来提供一种对Java集合运算和表达的高阶抽象. Stream的特性及优点: 无存储. Stream不是一种数据 ...

  8. 乐字节-Java8新特性-接口默认方法之Stream流(下)

    接上一篇:<Java8新特性之stream>,下面继续接着讲Stream 5.流的中间操作 常见的流的中间操作,归为以下三大类:筛选和切片流操作.元素映射操作.元素排序操作: 操作 描述 ...

  9. Java8新特性 Stream流式思想(二)

    如何获取Stream流刚开始写博客,有一些不到位的地方,还请各位论坛大佬见谅,谢谢! package cn.com.zq.demo01.Stream.test01.Stream; import org ...

随机推荐

  1. 如何解决 An error occured executing the Microsoft VC+runtime installer

    安装 postgresql 时遇见了 这个问题 There  has been an error.An error occured executing the Microsoft VC+ runtim ...

  2. Vue 路由切换时页面内容刷新页面并更新数据

    第二次进入页面,页面路由参数已经改变,但是页面内容不会刷新 <keep-alive>是Vue的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM <keep-aliv ...

  3. 数据结构(C++)——链栈

    结点结构 typedef char ElemType; typedef struct LkStackNode{ ElemType data; LkStackNode *next; }*Stack,SN ...

  4. react-native-image-picker用法

    1, 首先,安装下该插件. npm install react-native-image-picker@latest --save 2,自动安装(做了这一步 下面安装的平台设置大部分都自动添加好了) ...

  5. D. Tavas and Malekas 解析(字串匹配)

    Codeforce 535 D. Tavas and Malekas 解析(字串匹配) 今天我們來看看CF535D 題目連結 題目 給你一個字串$p$和一些$index$代表字串$p$在哪些位置會和長 ...

  6. java关键字之abstract

    Java 允许类,借口或成员方法具有抽象属性. abstract  修饰的类叫做抽象类,该类不能被实例化. abstract  修饰的方法叫抽象方法,抽象方法只有声明部分,没有具体的方法体. 接口总是 ...

  7. 对精致码农大佬的 [理解 volatile 关键字] 文章结论的思考和寻找真相

    一:背景 1. 讲故事 昨天在园里的编辑头条看到 精致码农大佬 写的一篇题为:[C#.NET 拾遗补漏]10:理解 volatile 关键字 (https://www.cnblogs.com/will ...

  8. MySQL安装及安装问题解答(二)

    在安装过程中难免会有一些异常情况出现,笔者对一部分异常情况做出解答以供参考 1.MySQL未能成功启动 在输入net start mysql后提示 MySQL 服务正在启动, MySQL 服务无法启动 ...

  9. 浅析 AC 自动机

    目录 简述 AC 自动机是什么 AC 自动机有什么用 AC 自动机·初探 AC 自动机·原理分析 AC 自动机·代码实现 AC 自动机·更进一步 第一题 第二题 第三题 从 AC 自动机到 fail ...

  10. P1098 字符串的展开

    P1098 字符串的展开 刷新三观的模拟题 题意描述 太长了自己去看吧. 算法分析 模拟题分析你*呀! 写这篇题解的唯一原因是:三目运算符用的好的话,可以让百行大模拟变成30行水题. 代码实现 #in ...