Java8用了这么久了,Stream 流用法及语法你都知道吗?
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 流用法及语法你都知道吗?的更多相关文章
- 深度分析:java8的新特性lambda和stream流,看完你学会了吗?
1. lambda表达式 1.1 什么是lambda 以java为例,可以对一个java变量赋一个值,比如int a = 1,而对于一个方法,一块代码也是赋予给一个变量的,对于这块代码,或者说被赋给变 ...
- Java8新特性之方法引用&Stream流
Java8新特性 方法引用 前言 什么是函数式接口 只包含一个抽象方法的接口,称为函数式接口. 可以通过 Lambda 表达式来创建该接口的对象.(若 Lambda 表达式抛出一个受检异常(即:非运行 ...
- JAVA8学习——从使用角度深入Stream流(学习过程)
Stream 流 初识Stream流 简单认识一下Stream:Stream类中的官方介绍: /** * A sequence of elements supporting sequential an ...
- Java8学习(4)-Stream流
Stream和Collection的区别是什么 流和集合的区别是什么? 粗略地说, 集合和流之间的差异就在于什么时候进行计算.集合是一个内存中的数据结构,它包含数据结构中目前所有的值--集合中的每个元 ...
- 这可能是史上最好的 Java8 新特性 Stream 流教程
本文翻译自 https://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/ 作者: @Winterbe 欢迎关注个人微信公众 ...
- java8 Stream的实现原理 (从零开始实现一个stream流)
1.Stream 流的介绍 1.1 java8 stream介绍 java8新增了stream流的特性,能够让用户以函数式的方式.更为简单的操纵集合等数据结构,并实现了用户无感知的并行计算. 1.2 ...
- Java8的Stream流(一) --- 基础用法
Java8中的Stream Stream使用一种类似用SQL语句从数据库查询数据的直观方式来提供一种对Java集合运算和表达的高阶抽象. Stream的特性及优点: 无存储. Stream不是一种数据 ...
- 乐字节-Java8新特性-接口默认方法之Stream流(下)
接上一篇:<Java8新特性之stream>,下面继续接着讲Stream 5.流的中间操作 常见的流的中间操作,归为以下三大类:筛选和切片流操作.元素映射操作.元素排序操作: 操作 描述 ...
- Java8新特性 Stream流式思想(二)
如何获取Stream流刚开始写博客,有一些不到位的地方,还请各位论坛大佬见谅,谢谢! package cn.com.zq.demo01.Stream.test01.Stream; import org ...
随机推荐
- find for /f 分割字符串 bat
@Echo off::总用例数For /f "tokens=2" %%i in ('Type bat.txt^|Find "Ran"') do (Echo %% ...
- 读高性能Mysql摘要
类型相关 INT(1)和INT(20)对于存储和计算来说,意义是相同的,他不会限制值的合法范围,只是一些交互工具会用来显示字符的个数 默认是有符号的,可以指定为无符号,增加数据存储范围,如0-255, ...
- Java学习的第五十一天
1.例9.3 析构函数 public class Cjava { public static void main(String[]args) { Student s1=new Student(1001 ...
- Java学习的第三十一天
1.使用RandomAccessFile随机读写文件 2.没有问题 3.明天学习综合实例
- Python使用协程进行爬虫
详情点我跳转 关注公众号"轻松学编程"了解更多. 1.协程 协程,又称微线程,纤程.英文名Coroutine. 协程是啥 ?? 首先我们得知道协程是啥?协程其实可以认为是比线程更小 ...
- 微信小程序跳转到微信公众号
我这里是uniapp里的操作 微信开发者工具配置 微信小程序官网地址:official-account 公众号关注组件. 当用户扫小程序码打开小程序时,开发者可在小程序内配置公众号关注组件,方便用户快 ...
- mns: Money Never Sleeps! 自己开发的一款 IDEA 插件介绍.
一边敲代码, 一边关注股票/基金行情, 还不怕同事盯到自己的屏幕! 对于一个关注股市跟基金的研发人员来说, 莫过于一天到晚写代码, 而不能及时的查看股市行情跟基金走势了吧. 写代码的时候比较容易忘记看 ...
- Java_包装类
包装类 在实际应用中, 经常需要把基本数据类型转化为对象以便操作. 因此, Java在设计类时, 为每个基本数据类型设计了一个对应的类进行包装, 这样八个和基本数据类型对应的类统称为包装类(Wrapp ...
- Lagrange插值C++程序
输入:插值节点数组.插值节点处的函数值数组,待求点 输出:函数值 代码如下:把printf的注释取消掉,能打印出中间计算过程,包括Lagrange多项式的求解,多项式每一项等等(代码多次修改,这些pr ...
- js常用的遍历方法以及flter,map方法
1.首先明确vue主要操作数据.他并不提倡操作dom. 数组的变异:能改变原数组. *** 先来复习下便利==遍历一个数组的四种方法: <script> let arr = [1, 2, ...