網(wǎng)上大部分文章介紹時(shí)概念性太強(qiáng),而實(shí)際例子又太少。只知道flatMap是流的扁平化抑堡。但是不知道這么去用涌矢。而這篇文章將以實(shí)際開(kāi)發(fā)的角度帶你去了解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()的作用是對(duì)流中的元素進(jìn)行1對(duì)多的轉(zhuǎn)換,然后將結(jié)果元素展開(kāi)為新的流娜庇。
這句話的含義是什么塔次?你可以理解為flatMap就是兩層for循環(huán)處理數(shù)據(jù)滨巴。而返回結(jié)果就是flatMap展開(kāi)的流。
1. 數(shù)據(jù)準(zhǔn)備
/**
* 玩具類
*/
@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("飛機(jī)", 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", "衡水中學(xué)", "尖子班"));
stus.add(new Student("jerry", "黃岡中學(xué)", "普通班"));
stus.add(new Student("dom", "衡水一中", "文科班"));
}
}
實(shí)戰(zhàn)使用
1. 對(duì)元素內(nèi)部的集合進(jìn)行處理
第一個(gè)Stream是將boys集合展開(kāi)俺叭,而在flatMap()內(nèi)部恭取,會(huì)將boys集合內(nèi)部集合再次展開(kāi)。
其等效于兩層for循環(huán)熄守,最終得到的結(jié)果是n*m給元素蜈垮。(注:n是外層循環(huán)次數(shù),m是內(nèi)層循環(huán)次數(shù))裕照。
public static void main(String[] args) {
//此時(shí)是處理第二個(gè)循環(huán)
List<Toy> toys = boys.stream().flatMap(
//將流中元素的數(shù)據(jù)再次展開(kāi)
b -> b.getToys().stream()).
collect(Collectors.toList());
System.out.println(JSON.toJSONString(toys));
// 等效于
//因?yàn)槭?:n的關(guān)系攒发,所以最終會(huì)產(chǎn)生n記錄
List<Toy> tls = new ArrayList<>();
for (Boy boy : boys) {
for (Toy toy : boy.getToys()) {
tls.add(toy);
}
}
//打印數(shù)據(jù)
System.out.println(JSON.toJSONString(tls));
}
當(dāng)然在flatMap方法內(nèi)部,可以利用這兩個(gè)流的元素進(jìn)行操作晋南。
public static void main(String[] args) {
List<String> collect1 = boys.stream().flatMap(b -> b.getToys().stream().
filter(t -> t.getPrice() > 12).
map(t -> {
//即可以操作外層元素也可以操作內(nèi)層元素
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. 用于兩個(gè)集合的合并
將兩個(gè)集合合并為一個(gè)集合惠猿,一般寫(xiě)法需要得借助兩層for循環(huán)。
而flatMap()操作的一個(gè)作用就是將兩個(gè)集合合并為一個(gè)集合负间,相當(dāng)于join操作偶妖,若沒(méi)有filter操作,會(huì)產(chǎn)生笛卡爾積現(xiàn)象政溃。
public static void main(String[] args) {
List<String> collect = boys.stream().flatMap(b -> stus.stream().
//第二層for循環(huán)中處理數(shù)據(jù)
filter(s -> b.getName().equals(s.getName())).
//將數(shù)據(jù)放入到一個(gè)新的集合中
map(Student::getName)
//結(jié)束第二層循環(huán)
).collect(Collectors.toList());
System.out.println(JSON.toJSONString(collect));
//等效于
List<String> data=new ArrayList<>();
//雙層for循環(huán)處理數(shù)據(jù)
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));
}
總結(jié):JDK1.8提供了Stream提供的流式操作可以使得開(kāi)發(fā)人員像操作數(shù)據(jù)庫(kù)一樣操作集合。Map相當(dāng)于select映射董虱,而flatMap更像join連接扼鞋。
3. 實(shí)戰(zhàn)運(yùn)用
場(chǎng)景:
表之間存在1:n的關(guān)系,理論上要?jiǎng)?chuàng)建2張表愤诱。但是若使用1張表云头,該表某個(gè)字段使用JSON格式,也可以完成淫半。
目前需求是在該表獲取到某些記錄后溃槐,需要轉(zhuǎn)換為n條記錄。
在數(shù)據(jù)庫(kù)獲取的實(shí)體是:
//實(shí)體對(duì)象
@Data
public static class Student {
private String name;
//JSON串撮慨,格式[1,2,4]
private String project;
}
分析:這個(gè)是對(duì)元素內(nèi)部的集合進(jìn)行處理竿痰。即也是雙層for循環(huán)。
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;
/**
* 測(cè)試合并流
*
*/
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循環(huán)是遍歷student對(duì)象
List<Student> collect = stus.stream().flatMap(s -> {
String project = s.getProject();
List<String> ps = JSONArray.parseArray(project, String.class);
//第二個(gè)for循環(huán)砌溺,是遍歷project對(duì)象影涉。
return ps.stream().map(p -> {
//每個(gè)Student的project填充的均是解析后的JSON串的值。
Student student = new Student();
BeanUtils.copyProperties(s, student);
//填充新的屬性
student.setProject(p);
return student;
});
}).collect(Collectors.toList()); //兩層for循環(huán)結(jié)束后組裝成List
System.out.println(collect);
}
//實(shí)體對(duì)象
@Data
public static class Student {
private String name;
//JSON串规伐,格式[1,2,4]
private String project;
}
}