Java Stream 使用手册

举报
苏州程序大白 发表于 2022/05/30 09:11:27 2022/05/30
【摘要】 Java Stream 使用手册在工作中时常会使用 Java Stream 对集合进行特殊操作,Stream 虽然能简化代码,但是书写以及阅读性不高。故在此记录常用的 Stream 案例以便在未来工作中查阅和使用(复制粘贴😅) 一、Stream 介绍Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用...

Java Stream 使用手册

在工作中时常会使用 Java Stream 对集合进行特殊操作,Stream 虽然能简化代码,但是书写以及阅读性不高。故在此记录常用的 Stream 案例以便在未来工作中查阅和使用(复制粘贴😅)

一、Stream 介绍

Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。

使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

1.1 Stream 操作流程

  1. 创建 Stream:一个数据源(如:集合、数组),获取一个流。
  2. 中间操作:一个中间操作链,对数据源的数据进行处理。
  3. 终止操作:一个终止操作,执行中间操作链,并产生结果。

1.2 Stream 的创建

通过 Collection 集合创建

// 获取顺序流
Stream<String> stream = list.stream();
// 获取并行流
Stream<String> parallelStream = list.parallelStream();
 复制

通过 Arrays 创建

String[] arr = {"hello","world","abc"};
Stream<String> stream = Arrays.stream(arr);
 复制

通过 Stream 的静态方法创建

Stream<String> stream1 = Stream.of("hello","world","abc");
// 获取无限流,迭代
Stream<Integer> stream2 = Stream.iterate(0,(x) -> x + 2);
// 获取无限流,生成
Stream<Integer> stream3 = Stream.generate(() -> (int)(Math.random()));
 复制

1.3 Stream 的中间操作

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理,而在终止操作时一次性全部处理,称为 “惰性求值”。

筛选与切片

方法 说明
filter(Predicate p) 接收 Lambda , 从流中过滤出元素
distinct() 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
limit(long maxSize) 截断流,使其元素不超过给定数量。
skip(long n) 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
@Test
public void test() {
	List<Person> list = Arrays.asList(
    			new Person(1,"aaa",21),
    			new Person(2,"bbb",22),
    			new Person(3,"ccc",23),
    			new Person(4,"ddd",24),
    			new Person(5,"eee",25)
			);
	// 中间操作
	Stream<Person> stream = list.stream()
								.filter((p) -> p.getAge() > 22) // 过滤得到 id 为 3、4、5 的元素
								.skip(1)                        // 跳过 1 个元素,得到 id 为 4、5 元素
								.limit(1);                      // 指获取 1 个元素,得到 id 为 4 的元素
	
	// 终止操作
	stream.forEach(System.out::println);
}
 复制

映射

方法 说明
map(Function f) 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
mapToDouble(ToDoubleFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。
mapToInt(ToIntFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。
mapToLong(ToLongFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。
flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
@Test
public void test() {
	List<String> list = Arrays.asList("aaa","bbb","ccc");
	
	// 中间操作
	Stream<String> stream = list.stream()
								.map((str) -> str.toUpperCase()); // 将流中所有元素进行转大写操作
	
	// 终止操作
	stream.forEach(System.out::println);
}
 复制

排序

方法 说明
sorted() 产生一个新流,其中按自然顺序排序
sorted(Comparator comp) 产生一个新流,其中按比较器顺序排序
@Test
public void test() {
	List<String> list = Arrays.asList("ccc","aaa","bbb");
	
	// 中间操作
	Stream<String> stream = list.stream().sorted(); // 自然排序
	
	// 终止操作
	stream.forEach(System.out::println);
}
 复制

1.4 Stream 的终止操作

终止操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void 。

查找与匹配

方法 说明
allMatch(Predicate p) 检查是否匹配所有元素。
anyMatch(Predicate p) 检查是否至少匹配一个元素。
noneMatch(Predicate p) 检查是否没有匹配所有元素。
findFirst() 返回第一个元素。
findAny() 返回当前流中的任意元素。
count() 返回流中元素总数。
max(Comparator c) 返回流中最大值。
min(Comparator c) 返回流中最小值。
forEach(Consumer c) 内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代——它帮你把迭代做了)。
@Test
public void test() {
	List<Person> list = Arrays.asList(
			new Person(1,"aaa",21),
			new Person(2,"bbb",22),
			new Person(3,"ccc",23),
			new Person(4,"ddd",24),
			new Person(5,"eee",25)
			);
	
	boolean result = list.stream()
						 .allMatch((p) -> p.getAge() > 22); // 是否所有元素中年龄大于 22
	
	System.out.println(result);
	
	Optional<Integer> op = list.stream()
							   .map(Person::getAge) // 获取所有元素的年龄
							   .max(Integer::compare); // 获取最大年龄

	System.out.println(op.get());
}
 复制

归约

方法 说明
reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值,返回 T。
reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值,返回 Optional。
@Test
public void test() {
	List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
	
	Integer result = list.stream().reduce(0, (x,y) -> x + y); // 所有元素进行累加
	
	System.out.println(result);
}
 复制

收集

方法 说明
collect(Collector c) 将流转换为其他形式。接收一个 Collector接口的实现,用于给 Stream 中元素做汇总的方法。
@Test
public void test() {
	List<Person> list = Arrays.asList(
			new Person(1,"aaa",21),
			new Person(2,"bbb",22),
			new Person(3,"ccc",23),
			new Person(4,"ddd",24),
			new Person(5,"eee",25)
			);
	
	List<String> result = list.stream()
							  .map(Person::getName)          // 获取所有元素的名字
							  .collect(Collectors.toList()); // 将名字从流中放到新的集合中
	
	System.out.println(result);
}
 复制

二、案例汇总

准备测试数据

List<User> userList = new ArrayList<>();
// id: id,name: 姓名,age: 年龄
User user1 = new User(1, "张三", 26);
User user2 = new User(2, "张三", 28);
User user3 = new User(3, "李四", 24);
User user4 = new User(4, "王五", 30);
User user5 = new User(4, "王五2", 31);
userList.add(user1);
userList.add(user2);
userList.add(user3);
userList.add(user4);
userList.add(user5);
 复制

2.1 获取 id 集合

List<Integer> idList = userList.stream().map(User::getId).collect(Collectors.toList());
System.out.println("idList:" + idList);
 复制

结果:

idList:[1, 2, 3, 4, 4]
 复制

2.2 拼接所有用户姓名

String names = userList.stream().map(User::getName).collect(Collectors.joining(","));
System.out.println("names:" + names);
 复制

结果:

names:张三,张三,李四,王五,王五2
 复制

2.3 年龄递增排序

List<User> sortList = userList.stream().sorted(Comparator.comparing(User::getAge)).collect(Collectors.toList());
System.out.println("sortList:" + sortList);
 复制

结果:

sortList:[User [id=3, name=李四, age=24], User [id=1, name=张三, age=26], User [id=2, name=张三, age=28], User [id=4, name=王五, age=30], User [id=4, name=王五2, age=31]]
 复制

如要递减,编写 Comparator.comparing(User::getAge).reversed()

2.4 过滤出年龄大于 30 的用户

List<User> filterList = userList.stream().filter(i -> i.getAge() > 30).collect(Collectors.toList());
System.out.println("filterList:" + filterList);
 复制

结果:

filterList:[User [id=4, name=王五2, age=31]]
 复制

2.5 统计年龄小于 30 的用户数量

long count = userList.stream().filter(i -> i.getAge() < 30).count();
System.out.println("count:" + count);
 复制

结果:

count:3
 复制

2.6 获取年龄最大用户

User maxUser = userList.stream().max(Comparator.comparing(User::getAge)).get();
System.out.println("maxUser:" + maxUser);
 复制

结果:

maxUser:User [id=4, name=王五2, age=31]
 复制

2.7 获取所有用户年龄总和

Integer totalAge = userList.stream().collect(Collectors.summingInt(User::getAge));
System.out.println("totalAge:" + totalAge);
 复制

结果:

totalAge:139
 复制

2.8 获取所有用户年龄平均值

Double averageAge = userList.stream().collect(Collectors.averagingDouble(User::getAge));
System.out.println("averageAge:" + averageAge);
 复制

结果:

averageAge:27.8
 复制

2.9 包含集合总数,年龄最大值, 年龄总和, 年龄平均值

DoubleSummaryStatistics statistics = userList.stream().collect(Collectors.summarizingDouble(User::getAge));
System.out.println("count:" + statistics.getCount() + ", max:" + statistics.getMax() + ", sum:" + statistics.getSum() + ", average:" + statistics.getAverage());
 复制

结果:

count:5, max:31.0, sum:139.0, average:27.8
 复制

2.10 List 转成 Map

Map<Integer, User> userMap = userList.stream()
                                     .collect(Collectors.toMap(User::getId, Function.identity(), (v1, v2) -> v2));
System.out.println("userMap:" + userMap);
 复制

结果:

userMap:{1=User [id=1, name=张三, age=26], 2=User [id=2, name=张三, age=28], 3=User [id=3, name=李四, age=24], 4=User [id=4, name=王五2, age=31]}
 复制

其中 (v1, v2) -> v2) 表示 v1,v2 相同时 取 v2

2.11 获取名字对应的的 id 集合

同样是 List 转成 Map 。

Map<String, List<Integer>> userMap = userList.stream()
                                             .collect(Collectors.groupingBy(
                                                User::getName, 
                                                Collectors.mapping(User::getId, Collectors.toList()))
                                             );
System.out.println("userMap:" + userMap);
 复制

结果:

userMap:{李四=[3], 王五2=[4], 张三=[1, 2], 王五=[4]}
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。