Stream API流学习总结

方法总览

图片来自网络

过滤-filter

假设有如下stream流

java 复制代码
record Fruit(String cname, String name, String category, String color) { }

Stream.of(
    new Fruit("草莓", "Strawberry", "浆果", "红色"),
    new Fruit("桑葚", "Mulberry", "浆果", "紫色"),
    new Fruit("杨梅", "Waxberry", "浆果", "红色"),
    new Fruit("核桃", "Walnut", "坚果", "棕色"),
    new Fruit("草莓", "Peanut", "坚果", "棕色"),
    new Fruit("蓝莓", "Blueberry", "浆果", "蓝色")
)

找出其中蓝色的浆果:

写法一:

java 复制代码
record Fruit(String cname, String name, String category, String color) { }

		Stream.of(
				new Fruit("草莓", "Strawberry", "浆果", "红色"),
				new Fruit("桑葚", "Mulberry", "浆果", "紫色"),
				new Fruit("杨梅", "Waxberry", "浆果", "红色"),
				new Fruit("核桃", "Walnut", "坚果", "棕色"),
				new Fruit("草莓", "Peanut", "坚果", "棕色"),
				new Fruit("蓝莓", "Blueberry", "浆果", "蓝色")
		).filter(f->f.category().equals("浆果")&&f.color().equals("蓝色"))
		 .forEach(System.out::println);

写法二:

java 复制代码
record Fruit(String cname, String name, String category, String color) {
		}

		Stream.of(
						new Fruit("草莓", "Strawberry", "浆果", "红色"),
						new Fruit("桑葚", "Mulberry", "浆果", "紫色"),
						new Fruit("杨梅", "Waxberry", "浆果", "红色"),
						new Fruit("核桃", "Walnut", "坚果", "棕色"),
						new Fruit("草莓", "Peanut", "坚果", "棕色"),
						new Fruit("蓝莓", "Blueberry", "浆果", "蓝色")
				).filter(f -> f.category().equals("浆果"))
				.filter(f -> f.color().equals("蓝色"))
				.forEach(System.out::println);
	}

映射-map

给所有的水果加上"酱"字:

java 复制代码
record Fruit(String cname, String name, String category, String color) {
		}

		Stream.of(
						new Fruit("草莓", "Strawberry", "浆果", "红色"),
						new Fruit("桑葚", "Mulberry", "浆果", "紫色"),
						new Fruit("杨梅", "Waxberry", "浆果", "红色"),
						new Fruit("核桃", "Walnut", "坚果", "棕色"),
						new Fruit("草莓", "Peanut", "坚果", "棕色"),
						new Fruit("蓝莓", "Blueberry", "浆果", "蓝色")
				)
				.map(f->f.cname()+"酱")
				.forEach(System.out::println);
	}

map相当于拿到每一个数据,然后执行箭头后面操作

打印结果如下:

降维-flatmap

java 复制代码
record Fruit(String cname, String name, String category, String color) {
		}
		Stream.of(
						List.of(
								new Fruit("草莓", "Strawberry", "浆果", "红色"),
								new Fruit("桑葚", "Mulberry", "浆果", "紫色"),
								new Fruit("杨梅", "Waxberry", "浆果", "红色"),
								new Fruit("蓝莓", "Blueberry", "浆果", "蓝色")
						),
						List.of(
								new Fruit("核桃", "Walnut", "坚果", "棕色"),
								new Fruit("草莓", "Peanut", "坚果", "棕色")
						)
				)
				.flatMap(list -> list.stream())
				.forEach(System.out::println);

相当于将两个集合里面的数据提取出来,组成一个新的流。

构建流

根据已有的数组构建流:

java 复制代码
Arrays.stream(array)

根据已有的 Collection 构建流(包括 List,Set 等):

java 复制代码
List.of("a","b","c").stream()

把一个对象变成流:

java 复制代码
Stream.of("d")

把多个对象变成流:

java 复制代码
Stream.of("x", "y")

废话不多数,关于构建流,直接看代码:

java 复制代码
//1.根据集合构建流
		//这里的stream()是Collection接口的一个方法,所有单列集合的所有实现类都可以转换成流
		//包括list,set等集合
		List.of(1, 2, 3).stream().forEach(System.out::println);

		//2.双列集合没有stream方法,但是我们可以将双列集合转换成单列集合,再调用stream方法
		Map.of("a", 1, "b", 2)
				.entrySet()//转换成键值对对象
				.stream()//此时map集合是set集合的形态,可以转为流了
				.forEach(System.out::println);//打印

		//3.根据数组构建流
		int[] array = {1, 2, 3};
		Arrays.stream(array)
				.forEach(System.out::println);

		//4.根据对象创建流
		Stream.of(1, 2, 3).forEach(System.out::println);

合并和截取

java 复制代码
//1.合并
		Stream<Integer> s1 = Stream.of(1, 2, 3);
		Stream<Integer> s2 = Stream.of(4, 5);

		Stream<Integer> concat = Stream.concat(s1, s2);
		//concat.forEach(System.out::println);

		//2.截取-直接给出截取位置
		//skip(long n) 跳过n个数据,保留剩下的数据
		//limit(long n) 保留n个数据,其他的舍弃
		//concat.skip(2).forEach(System.out::println);//打印3,4,5
		//concat.limit(2).forEach(System.out::println);//打印1,2

		//留下中间的3,4的写法
		//concat.skip(2).limit(2).forEach(System.out::println);

		//3.截取-根据条件确定截取位置
		//dropWhile 是 drop 流中元素,直到条件不成立,留下剩余元素
		//takeWhile 是 take 流中元素,直到条件不成立,舍弃剩余元素
		//concat.takeWhile(x -> x < 3).forEach(System.out::println);
		//concat.dropWhile(x -> x < 3).forEach(System.out::println);

生成流

生成从 0 ~ 9 的数字(含头不含尾)

java 复制代码
IntStream.range(0, 10)

或者

java 复制代码
IntStream.rangeClosed(0, 9)

如果想订制,可以用 iterate 方法,例如下面生成奇数序列

java 复制代码
IntStream.iterate(1, x -> x + 2)
  • 参数1 是初始值

  • 参数2 是一个特殊 Function,即参数类型与返回值相同,它会根据上一个元素 x 的值计算出当前元素

  • 需要用 limit 限制元素个数

也可以用 iterate 的重载方法

java 复制代码
IntStream.iterate(1, x -> x < 10, x -> x + 2)

iterate 的特点是根据上一个元素计算当前元素,如果不需要依赖上一个元素,可以改用 generate 方法

例如下面是生成 5 个随机 int

java 复制代码
Stream.generate(()-> ThreadLocalRandom.current().nextInt()).limit(5)

不过如果只是生成随机数的话,有更简单的办法

java 复制代码
ThreadLocalRandom.current().ints(5)

如果要指定上下限,例如下面是生成从 0~9 的100个随机数

java 复制代码
ThreadLocalRandom.current().ints(100, 0, 10)

查找和判断

下面的代码找到流中任意(Any)一个偶数:

java 复制代码
int[] array = {1, 3, 5, 4, 7, 6, 9};

Arrays.stream(array)
    .filter(x -> (x & 1) == 0)
    .findAny()
    .ifPresent(System.out::println);

查找的案例:

java 复制代码
		//1.findFirst() 返回符合要求的第一个值
		IntStream stream = IntStream.of(1, 2, 3, 4 5);
		//System.out.println(stream.filter(x -> (x & 1) == 0).findFirst().orElse(-1));
		//解释:.filter(x -> (x & 1) == 0)表示查找流里面是否有偶数
		//.findFirst()表示将符合要求的数据中返回第一个元素
		//.orElse(-1)表示如果没有符合要求的数据,则返回值-1
		stream.filter(x -> (x & 1) == 0).findFirst().ifPresent((x) ->        System.out.println(x));
		//.ifPresent((x) -> System.out.println(x)表示如果存在符合的数据,则打印出来


		//2.findAny找到任意一个元素即可(并行流中可以看出和findFirst的区别)
		stream.filter(x -> (x & 1) == 0).findAny().ifPresent((x) -> System.out.println(x));

判断的案例:

java 复制代码
		//1.findFirst() 返回符合要求的第一个值
		IntStream stream = IntStream.of(1, 2, 3, 4, 5);

		//System.out.println(stream.anyMatch(x -> (x & 1) == 0));//true
		//System.out.println(stream.allMatch(x -> (x & 1) == 0));//false
		System.out.println(stream.noneMatch(x -> (x & 1) == 0));//false

去重和排序

java 复制代码
		//去重
		//IntStream.of(1, 2, 3, 1, 2, 3, 4, 5)
		//		.distinct()
		//		.forEach(System.out::println);

		//排序
		record Hero(String name, int strength) {
		}

		Stream.of(
						new Hero("独孤求败", 100),
						new Hero("令狐冲", 90),
						new Hero("风清扬", 98),
						new Hero("东方不败", 98),
						new Hero("方证", 92),
						new Hero("任我行", 92),
						new Hero("冲虚", 90),
						new Hero("向问天", 88),
						new Hero("不戒", 88)
				)
				.sorted((a, b) -> a.strength() < b.strength() ? -1 : a.strength() == b.strength() ? 0 : 1)
				.forEach(System.out::println);

化简

reduce(init, (p,x) -> r)

  • init 代表初始值

  • (p,x) -> r 是一个 BinaryOperator,作用是根据上次化简结果 p 和当前元素 x,得到本次化简结果 r

这样两两化简,可以将流中的所有元素合并成一个结果

java 复制代码
record Hero(String name, int strength) {
		}

		Stream.of(
				new Hero("独孤求败", 100),
				new Hero("令狐冲", 90),
				new Hero("风清扬", 98),
				new Hero("东方不败", 98),
				new Hero("方证", 92),
				new Hero("任我行", 92),
				new Hero("冲虚", 90),
				new Hero("向问天", 88),
				new Hero("不戒", 88)
		).reduce((h1, h2) -> h1.strength() > h2.strength() ? h1 : h2);

收集

java 复制代码
collect( supplier, accumulator, combiner)

* supplier 是描述如何创建收集容器 c :`()-> c`
* accumulator 是描述如何向容器 c 添加元素 x:`(c, x) -> void`
* combiner 是描述如何合并两个容器:`(c1, c2) -> void`
  * 串行流下不需要合并容器
  * 并行流如果用的是并发容器,也不需要合并
相关推荐
YuTaoShao2 小时前
【LeetCode 热题 100】131. 分割回文串——回溯
java·算法·leetcode·深度优先
源码_V_saaskw2 小时前
JAVA图文短视频交友+自营商城系统源码支持小程序+Android+IOS+H5
java·微信小程序·小程序·uni-app·音视频·交友
超浪的晨2 小时前
Java UDP 通信详解:从基础到实战,彻底掌握无连接网络编程
java·开发语言·后端·学习·个人开发
双力臂4043 小时前
Spring Boot 单元测试进阶:JUnit5 + Mock测试与切片测试实战及覆盖率报告生成
java·spring boot·后端·单元测试
Edingbrugh.南空3 小时前
Aerospike与Redis深度对比:从架构到性能的全方位解析
java·开发语言·spring
QQ_4376643144 小时前
C++11 右值引用 Lambda 表达式
java·开发语言·c++
永卿0014 小时前
设计模式-迭代器模式
java·设计模式·迭代器模式
誰能久伴不乏4 小时前
Linux如何执行系统调用及高效执行系统调用:深入浅出的解析
java·服务器·前端
慕y2744 小时前
Java学习第七十二部分——Zookeeper
java·学习·java-zookeeper
midsummer_woo4 小时前
基于spring boot的医院挂号就诊系统(源码+论文)
java·spring boot·后端