Java Stream 中间操作
字数: 0 字 时长: 0 分钟
1. 概述
Stream 的中间操作用于对流中的数据进行处理。
常见的中间操作包括:
filter
map
distinct
sorted
limit
skip
flatMap中间操作通常会返回一个新的 Stream,因此可以继续进行链式调用:
authors.stream()
.distinct()
.filter(author -> author.getAge() < 18)
.map(author -> author.getName());中间操作具有惰性,如果没有终结操作,通常不会真正执行。
2. filter()
filter() 用于根据条件过滤流中的元素。
符合条件的元素会继续保留在 Stream 中,不符合条件的元素会被过滤掉。
例如:打印姓名长度大于 1 的作者姓名。
List<Author> authors = getAuthors();
authors.stream()
.filter(author -> author.getName().length() > 1)
.forEach(author -> System.out.println(author.getName()));执行过程:
所有 Author
↓
判断 name.length() > 1
↓
保留符合条件的 Author3. map()
map() 用于对 Stream 中的元素进行转换或计算。
可以理解为:
原来的元素
↓
执行转换
↓
新的元素3.1 类型转换
例如将 Author 转换成作者姓名 String:
List<Author> authors = getAuthors();
authors.stream()
.map(author -> author.getName())
.forEach(name -> System.out.println(name));此时 Stream 中元素的类型发生了变化:
Stream<Author>
↓
map()
↓
Stream<String>3.2 数据计算
例如获取作者年龄,并将年龄增加 10:
List<Author> authors = getAuthors();
authors.stream()
.map(author -> author.getAge())
.map(age -> age + 10)
.forEach(age -> System.out.println(age));执行过程:
Author
↓
Integer 年龄
↓
年龄 + 104. distinct()
distinct() 用于去除 Stream 中的重复元素。
例如打印所有作者姓名,并去除重复作者:
List<Author> authors = getAuthors();
authors.stream()
.distinct()
.forEach(author -> System.out.println(author.getName()));对于自定义对象,distinct() 判断元素是否重复依赖:
equals()
hashCode()因此实体类需要正确实现 equals() 和 hashCode()。
例如使用 Lombok:
@EqualsAndHashCode
public class Author {
}
distinct()能否正确去重,取决于对象的equals()和hashCode()实现。
5. sorted()
sorted() 用于对 Stream 中的元素进行排序。
主要有两种形式:
sorted()sorted(Comparator)5.1 使用 Comparator 排序
例如:
对作者进行去重,然后按照年龄降序排序。
List<Author> authors = getAuthors();
authors.stream()
.distinct()
.sorted((o1, o2) -> o2.getAge().compareTo(o1.getAge()))
.forEach(author -> System.out.println(author));其中:
o2.getAge().compareTo(o1.getAge())表示按照年龄降序排列。
5.2 使用自然排序
如果直接调用:
sorted()Stream 中的对象需要支持自然排序。
例如让 Author 实现 Comparable:
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class Author implements Comparable<Author> {
private Long id;
private String name;
private Integer age;
private String intro;
private List<Book> books;
@Override
public int compareTo(Author o) {
return this.age.compareTo(o.getAge());
}
}之后就可以直接调用:
List<Author> authors = getAuthors();
authors.stream()
.distinct()
.sorted()
.forEach(author -> System.out.println(author.getAge()));6. limit()
limit() 用于限制 Stream 中最多保留多少个元素。
基本格式:
limit(n)例如:
对作者进行去重,按照年龄降序排序,然后打印年龄最大的两个作者。
List<Author> authors = getAuthors();
authors.stream()
.distinct()
.sorted((o1, o2) -> o2.getAge().compareTo(o1.getAge()))
.limit(2)
.forEach(author -> System.out.println(author.getName()));执行过程:
作者集合
↓
去重
↓
年龄降序排序
↓
保留前 2 个7. skip()
skip() 用于跳过 Stream 前面的若干个元素。
基本格式:
skip(n)例如:
对作者进行去重并按照年龄降序排序,然后跳过年龄最大的作者。
List<Author> authors = getAuthors();
authors.stream()
.distinct()
.sorted((o1, o2) -> o2.getAge().compareTo(o1.getAge()))
.skip(1)
.forEach(author -> System.out.println(author.getName()));执行过程:
年龄降序后的作者
↓
skip(1)
↓
跳过年龄最大的作者8. flatMap()
map() 通常是把一个元素转换成另一个元素:
一个元素
↓
一个元素而 flatMap() 可以将一个元素转换成一个 Stream,然后把多个 Stream 展开并合并成一个 Stream。
例如:
Author
↓
List<Book>
↓
Stream<Book>
↓
合并
↓
一个 Stream<Book>8.1 获取所有作者的书籍
要求:
打印所有书籍名称,并对重复书籍进行去重。
List<Author> authors = getAuthors();
authors.stream()
.flatMap(author -> author.getBooks().stream())
.distinct()
.forEach(book -> System.out.println(book.getName()));原来每个 Author 都拥有自己的:
List<Book>通过:
author.getBooks().stream()转换为:
Stream<Book>然后由 flatMap() 将这些 Stream 合并成一个大的:
Stream<Book>8.2 flatMap() 完整写法
Lambda:
.flatMap(author -> author.getBooks().stream())对应的匿名内部类写法:
List<Author> authors = getAuthors();
authors.stream()
.flatMap(new Function<Author, Stream<Book>>() {
@Override
public Stream<Book> apply(Author author) {
return author.getBooks().stream();
}
})
.distinct()
.forEach(new Consumer<Book>() {
@Override
public void accept(Book book) {
System.out.println(book.getName());
}
});8.3 获取所有书籍分类
书籍中的分类数据类似:
哲学,爱情
个人成长,爱情
爱情,个人传记现在要求打印所有分类,并去重,最终不能出现:
哲学,爱情而是拆分成:
哲学
爱情
个人成长
个人传记实现:
List<Author> authors = getAuthors();
authors.stream()
.flatMap(author -> author.getBooks().stream())
.distinct()
.flatMap(book -> Arrays.stream(book.getCategory().split(",")))
.distinct()
.forEach(category -> System.out.println(category));这里进行了两次展开:
Author
↓
flatMap()
↓
Book
↓
获取 category
↓
split(",")
↓
flatMap()
↓
String 分类
flatMap()的核心作用就是把多层结构中的数据展开成一个 Stream。
9. 常用中间操作小结
| 方法 | 作用 |
|---|---|
filter() | 按条件过滤元素 |
map() | 对元素进行转换或计算 |
distinct() | 去除重复元素 |
sorted() | 对元素进行排序 |
limit() | 保留前 N 个元素 |
skip() | 跳过前 N 个元素 |
flatMap() | 展开多层数据结构 |
可以简单理解为:
filter → 筛选
map → 转换
distinct → 去重
sorted → 排序
limit → 截取前 N 个
skip → 跳过前 N 个
flatMap → 展开这些操作都属于中间操作,可以继续返回 Stream 进行链式调用,最终需要通过终结操作触发执行。