你好,我是看山。
本文收录在 《从小工到专家的 Java 进阶之旅》 系列专栏。日拱一卒,功不唐捐。
一、Stream API 简介
Stream API 为处理集合数据提供了一种高效且简洁的方式。它允许我们以一种声明式的风格来操作数据,避免了传统的迭代器和循环方式带来的繁琐和易错性。
Stream API 可以对集合进行过滤、映射、归约等多种操作,而其中的 map
和 flatMap
操作在数据转换和处理复杂数据结构方面发挥着重要作用。
二、map 操作
基本概念
map
操作是 Stream API 中最常用的操作之一。它接收一个 Function
作为参数,这个函数会被应用到流中的每一个元素上,从而将流中的元素转换为新的元素。
例如,我们有一个包含整数的流,想要将每个整数都乘以 2:
public class MapExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> result = numbers.stream()
.map(n -> n * 2)
.collect(Collectors.toList());
System.out.println(result);
}
}
在上述代码中,map(n -> n * 2)
将流中的每个整数元素 n
转换为 n * 2
。
特点
- 元素一对一转换:
map
操作保持了流中元素的数量不变,只是对每个元素进行了单独的转换。 - 简单的数据转换:适用于简单的数据类型转换,比如将字符串转换为大写、将数字进行简单的数学运算等。
应用场景
- 数据类型转换:从一种数据类型转换为另一种数据类型。例如,将一个包含
Double
类型元素的流转换为包含Integer
类型元素的流。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class MapDataTypeConversion {
public static void main(String[] args) {
List<Double> doubles = Arrays.asList(1.1, 2.2, 3.3);
List<Integer> integers = doubles.stream()
.map(d -> d.intValue())
.collect(Collectors.toList());
System.out.println(integers);
}
}
- 对象属性提取:当我们有一个对象流时,可以使用
map
操作提取对象的某个属性值,形成一个新的流。
import java.util.ArrayList;
import java.util.List;
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
public class MapObjectPropertyExtraction {
public static void main(String[] args) {
List<Person> people = new ArrayList<>();
people.add(new Person("Alice", 25));
people.add(new Person("Bob", 30));
List<String> names = people.stream()
.map(Person::getName)
.collect(Collectors.toList());
System.out.println(names);
}
}
三、flatMap 操作
基本概念
flatMap
操作比 map
操作更为复杂。它接收一个函数作为参数,这个函数将流中的每个元素转换为一个流,然后将这些流合并(扁平化)为一个单一的流。
例如,我们有一个包含多个字符串的流,每个字符串都包含一些用逗号分隔的单词,我们想要将所有的单词提取到一个流中:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class FlatMapExample {
public static void main(String[] args) {
List<String> strings = Arrays.asList("apple,banana", "cherry,date");
List<String> words = strings.stream()
.flatMap(s -> Arrays.stream(s.split(",")))
.collect(Collectors.toList());
System.out.println(words);
}
}
在上述代码中,flatMap(s -> Arrays.stream(s.split(","))
将每个字符串 s
转换为一个包含其分割后的单词的流,然后将这些流合并为一个单一的流。
特点
- 处理嵌套流:
flatMap
主要用于处理嵌套的流结构,将嵌套的流扁平化。 - 元素数量可能改变:与
map
不同,flatMap
操作后的流中的元素数量可能会发生变化,取决于内部流的元素数量。
应用场景
- 处理二维数组或多维数组:将二维数组或多维数组转换为一维数组。
import java.util.Arrays;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class FlatMap2DArray {
public static void main(String[] args) {
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
// 将二维数组转换为一维数组
int[] flatArray = Arrays.stream(matrix)
.flatMapToInt(IntStream::of)
.toArray();
System.out.println(Arrays.toString(flatArray));
}
}
- 处理复杂的数据结构:当数据结构中包含嵌套的集合时,使用
flatMap
可以方便地将所有元素提取到一个流中进行处理。
import java.util.ArrayList;
import java.util.List;
class Department {
private String name;
private List<Employee> employees;
public Department(String name, List<Employee> employees) {
this.name = name;
this.employees = employees;
}
public List<Employee> getEmployees() {
return employees;
}
}
class Employee {
private String name;
public Employee(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class FlatMapComplexDataStructure {
public static void main(String[] args) {
List<Department> departments = new ArrayList<>();
List<Employee> employees1 = new ArrayList<>();
employees1.add(new Employee("Alice"));
employees1.add(new Employee("Bob"));
List<Employee> employees2 = new ArrayList<>();
employees2.add(new Employee("Charlie"));
employees2.add(new Employee("David"));
departments.add(new Department("HR", employees1));
departments.add(new Department("IT", employees2));
// 提取所有员工的名字到一个流中
List<String> allEmployeeNames = departments.stream()
.flatMap(d -> d.getEmployees().stream())
.map(Employee::getName)
.collect(Collectors.toList());
System.out.println(allEmployeeNames);
}
}
四、map 和 flatMap 的对比
操作对象
map
:操作的是单个元素,将单个元素转换为另一个元素。flatMap
:操作的是流中的每个元素,这些元素本身也是流(或者可以转换为流),然后将这些内部流合并为一个单一的流。
返回结果
map
:返回的流中的元素是对原始流中元素进行转换后的结果,元素数量不变。flatMap
:返回的是一个将内部流扁平化后的流,元素数量可能会改变。
文末总结
在 Stream API 中,map
和 flatMap
都是非常重要的数据转换操作。
map
适用于简单的一对一元素转换场景,而 flatMap
则在处理嵌套流和复杂数据结构时具有独特的优势。
通过合理地选择和使用这两个操作,可以更高效地处理集合数据,实现简洁、高效和可读的代码。
在实际编程中,根据数据的结构和处理需求,灵活运用 map
和 flatMap
能够使我们更好地利用 Stream API 进行数据处理。
青山不改,绿水长流,我们下次见。