网上大部分文章介绍时概念性太强,而实际例子又太少。只知道flatMap是流的扁平化。但是不知道这么去用。而这篇文章将以实际开发的角度带你去了解flatMap。
源码中的描述:
The code flatMap() operation has the effect of applying a one-to-many transformation to the elements of the stream, and then flattening the resulting elements into a new stream.
flatMap()的作用是对流中的元素进行1对多的转换,然后将结果元素展开为新的流。
这句话的含义是什么?你可以理解为flatMap就是两层for循环处理数据。而返回结果就是flatMap展开的流。
1. 数据准备
/**
* 玩具类
*/
@Data
@AllArgsConstructor
public class Toy {
private String name;
private double price;
}
@Data
@AllArgsConstructor
public class Boy {
private String name;
private int age;
private List<Toy> toys;
}
@Data
@AllArgsConstructor
public class Student {
private String name;
private String schoolName;
private String className;
}
public class TestStream {
private static List<Boy> boys = new ArrayList<>();
private static List<Student> stus = new ArrayList<>();
static {
boys.add(new Boy("tom", 12,
Arrays.asList(
new Toy("飞机", 9.1),
new Toy("坦克", 7.0),
new Toy("小熊", 14.0))));
boys.add(new Boy("tony", 19,
Arrays.asList(
new Toy("玩具枪", 13.1),
new Toy("小汽车", 12.2))));
stus.add(new Student("tom", "衡水中学", "尖子班"));
stus.add(new Student("jerry", "黄冈中学", "普通班"));
stus.add(new Student("dom", "衡水一中", "文科班"));
}
}
实战使用
1. 对元素内部的集合进行处理
第一个Stream是将boys集合展开,而在flatMap()内部,会将boys集合内部集合再次展开。
其等效于两层for循环,最终得到的结果是n*m给元素。(注:n是外层循环次数,m是内层循环次数)。
public static void main(String[] args) {
//此时是处理第二个循环
List<Toy> toys = boys.stream().flatMap(
//将流中元素的数据再次展开
b -> b.getToys().stream()).
collect(Collectors.toList());
System.out.println(JSON.toJSONString(toys));
// 等效于
//因为是1:n的关系,所以最终会产生n记录
List<Toy> tls = new ArrayList<>();
for (Boy boy : boys) {
for (Toy toy : boy.getToys()) {
tls.add(toy);
}
}
//打印数据
System.out.println(JSON.toJSONString(tls));
}
当然在flatMap方法内部,可以利用这两个流的元素进行操作。
public static void main(String[] args) {
List<String> collect1 = boys.stream().flatMap(b -> b.getToys().stream().
filter(t -> t.getPrice() > 12).
map(t -> {
//即可以操作外层元素也可以操作内层元素
return b.getName() + ":" + t.getName();
})).collect(Collectors.toList());
System.out.println(JSON.toJSONString(collect1));
//等效于:
List<String> tls1 = new ArrayList<>();
for (Boy boy : boys) {
for (Toy toy : boy.getToys()) {
if (toy.getPrice() > 12) {
tls1.add(boy.getName() + ":" + toy.getName());
}
}
}
System.out.println(JSON.toJSONString(tls1));
}
}
2. 用于两个集合的合并
将两个集合合并为一个集合,一般写法需要得借助两层for循环。
而flatMap()操作的一个作用就是将两个集合合并为一个集合,相当于join操作,若没有filter操作,会产生笛卡尔积现象。
public static void main(String[] args) {
List<String> collect = boys.stream().flatMap(b -> stus.stream().
//第二层for循环中处理数据
filter(s -> b.getName().equals(s.getName())).
//将数据放入到一个新的集合中
map(Student::getName)
//结束第二层循环
).collect(Collectors.toList());
System.out.println(JSON.toJSONString(collect));
//等效于
List<String> data=new ArrayList<>();
//双层for循环处理数据
for (Boy boy : boys) {
for (Student student : stus) {
//filter条件
if(boy.getName().equals(student.getName())){
//map子句,将元素放入到新集合中
data.add(student.getName());
}
}
}
System.out.println(JSON.toJSONString(data));
}
总结:JDK1.8提供了Stream提供的流式操作可以使得开发人员像操作数据库一样操作集合。Map相当于select映射,而flatMap更像join连接。
3. 实战运用
场景:
表之间存在1:n的关系,理论上要创建2张表。但是若使用1张表,该表某个字段使用JSON格式,也可以完成。
目前需求是在该表获取到某些记录后,需要转换为n条记录。
在数据库获取的实体是:
//实体对象
@Data
public static class Student {
private String name;
//JSON串,格式[1,2,4]
private String project;
}
分析:这个是对元素内部的集合进行处理。即也是双层for循环。
import com.alibaba.fastjson.JSONArray;
import lombok.Data;
import org.springframework.beans.BeanUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* 测试合并流
*
*/
public class TestFlatMap {
public static List<Student> stus = new ArrayList<>();
static {
Student s1 = new Student();
s1.setName("黎明");
s1.setProject("[1,3,5,6]");
Student s2 = new Student();
s2.setName("李白");
s2.setProject("[2,9]");
stus.add(s1);
stus.add(s2);
}
public static void main(String[] args) {
test1();
}
public static void test1() {
//第一层for循环是遍历student对象
List<Student> collect = stus.stream().flatMap(s -> {
String project = s.getProject();
List<String> ps = JSONArray.parseArray(project, String.class);
//第二个for循环,是遍历project对象。
return ps.stream().map(p -> {
//每个Student的project填充的均是解析后的JSON串的值。
Student student = new Student();
BeanUtils.copyProperties(s, student);
//填充新的属性
student.setProject(p);
return student;
});
}).collect(Collectors.toList()); //两层for循环结束后组装成List
System.out.println(collect);
}
//实体对象
@Data
public static class Student {
private String name;
//JSON串,格式[1,2,4]
private String project;
}
}