在工作中时常会使用 Java Stream 对集合进行特殊操作,Stream 虽然能简化代码,但是书写以及阅读性不高。故在此记录常用的 Stream 案例以便在未来工作中查阅和使用(复制粘贴😅)
一、Stream 介绍 {#一、Stream-介绍}
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。
使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
1.1 Stream 操作流程 {#1.1-Stream-操作流程}
-
创建 Stream:一个数据源(如:集合、数组),获取一个流。
-
中间操作:一个中间操作链,对数据源的数据进行处理。
-
终止操作:一个终止操作,执行中间操作链,并产生结果。
1.2 Stream 的创建 {#1.2-Stream-的创建}
通过 Collection 集合创建
|-----------------|-------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4
| // 获取顺序流 Stream<String> stream = list.stream(); // 获取并行流 Stream<String> parallelStream = list.parallelStream();
|
通过 Arrays 创建
|-------------|---------------------------------------------------------------------------------------------|
| 1 2
| String[] arr = {"hello","world","abc"}; Stream<String> stream = Arrays.stream(arr);
|
通过 Stream 的静态方法创建
|-------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5
| 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 的中间操作 {#1.3-Stream-的中间操作}
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理,而在终止操作时一次性全部处理,称为 "惰性求值"。
筛选与切片
| 方法 | 说明 | |---------------------|---------------------------------------------------------| | filter(Predicate p) | 接收 Lambda , 从流中过滤出元素 | | distinct() | 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素 | | limit(long maxSize) | 截断流,使其元素不超过给定数量。 | | skip(long n) | 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补 |
|------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @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) | 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流 |
|---------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11
| @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) | 产生一个新流,其中按比较器顺序排序 |
|------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10
| @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 的终止操作 {#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 使用内部迭代------它帮你把迭代做了)。 |
|---------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @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 。 |
|-------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8
| @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 中元素做汇总的方法。 |
|------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @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); }
|
二、案例汇总 {#二、案例汇总}
准备测试数据
|------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12
| 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 集合 {#2.1-获取-id-集合}
|-------------|-----------------------------------------------------------------------------------------------------------------------------------------|
| 1 2
| List<Integer> idList = userList.stream().map(User::getId).collect(Collectors.toList()); System.out.println("idList:" + idList);
|
结果:
|-----------|--------------------------------|
| 1
| idList:[1, 2, 3, 4, 4]
|
2.2 拼接所有用户姓名 {#2.2-拼接所有用户姓名}
|-------------|-------------------------------------------------------------------------------------------------------------------------------------|
| 1 2
| String names = userList.stream().map(User::getName).collect(Collectors.joining(",")); System.out.println("names:" + names);
|
结果:
|-----------|-------------------------------|
| 1
| names:张三,张三,李四,王五,王五2
|
2.3 年龄递增排序 {#2.3-年龄递增排序}
|-------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2
| List<User> sortList = userList.stream().sorted(Comparator.comparing(User::getAge)).collect(Collectors.toList()); System.out.println("sortList:" + sortList);
|
结果:
|-----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1
| 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 的用户 {#2.4-过滤出年龄大于-30-的用户}
|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2
| List<User> filterList = userList.stream().filter(i -> i.getAge() > 30).collect(Collectors.toList()); System.out.println("filterList:" + filterList);
|
结果:
|-----------|----------------------------------------------------|
| 1
| filterList:[User [id=4, name=王五2, age=31]]
|
2.5 统计年龄小于 30 的用户数量 {#2.5-统计年龄小于-30-的用户数量}
|-------------|--------------------------------------------------------------------------------------------------------------------|
| 1 2
| long count = userList.stream().filter(i -> i.getAge() < 30).count(); System.out.println("count:" + count);
|
结果:
|-----------|-----------------|
| 1
| count:3
|
2.6 获取年龄最大用户 {#2.6-获取年龄最大用户}
|-------------|-----------------------------------------------------------------------------------------------------------------------------------|
| 1 2
| User maxUser = userList.stream().max(Comparator.comparing(User::getAge)).get(); System.out.println("maxUser:" + maxUser);
|
结果:
|-----------|-----------------------------------------------|
| 1
| maxUser:User [id=4, name=王五2, age=31]
|
2.7 获取所有用户年龄总和 {#2.7-获取所有用户年龄总和}
|-------------|----------------------------------------------------------------------------------------------------------------------------------------|
| 1 2
| Integer totalAge = userList.stream().collect(Collectors.summingInt(User::getAge)); System.out.println("totalAge:" + totalAge);
|
结果:
|-----------|----------------------|
| 1
| totalAge:139
|
2.8 获取所有用户年龄平均值 {#2.8-获取所有用户年龄平均值}
|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2
| Double averageAge = userList.stream().collect(Collectors.averagingDouble(User::getAge)); System.out.println("averageAge:" + averageAge);
|
结果:
|-----------|-------------------------|
| 1
| averageAge:27.8
|
2.9 包含集合总数,年龄最大值, 年龄总和, 年龄平均值 {#2.9-包含集合总数,年龄最大值,-年龄总和,-年龄平均值}
|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2
| DoubleSummaryStatistics statistics = userList.stream().collect(Collectors.summarizingDouble(User::getAge)); System.out.println("count:" + statistics.getCount() + ", max:" + statistics.getMax() + ", sum:" + statistics.getSum() + ", average:" + statistics.getAverage());
|
结果:
|-----------|----------------------------------------------------|
| 1
| count:5, max:31.0, sum:139.0, average:27.8
|
2.10 List 转成 Map {#2.10-List-转成-Map}
|---------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3
| Map<Integer, User> userMap = userList.stream() .collect(Collectors.toMap(User::getId, Function.identity(), (v1, v2) -> v2)); System.out.println("userMap:" + userMap);
|
结果:
|-----------|---------------------------------------------------------------------------------------------------------------------------------------------------|
| 1
| 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 集合 {#2.11-获取名字对应的的-id-集合}
同样是 List 转成 Map 。
|---------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6
| Map<String, List<Integer>> userMap = userList.stream() .collect(Collectors.groupingBy( User::getName, Collectors.mapping(User::getId, Collectors.toList())) ); System.out.println("userMap:" + userMap);
|
结果:
|-----------|------------------------------------------------------|
| 1
| userMap:{李四=[3], 王五2=[4], 张三=[1, 2], 王五=[4]}
|