1

Java Stream 使用手册 | 月光中的污点

 3 years ago
source link: https://www.extlight.com/2021/07/01/Java-Stream-%E4%BD%BF%E7%94%A8%E6%89%8B%E5%86%8C/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
loading2.jpg

『 Java Stream 使用手册 』

2021 年 07 月 01 日   50 浏览

在工作中时常会使用 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]}
复制

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK