map比较简单,可以抽取列表元素的某个属性形成新的列表,但是无法对更深层的属性做提取,相当于只针对一维数组,一对一的处理。
flatMap主要是对流进行扁平化,可以将一个2维的集合映射成一个一维,相当于他映射的深度比map深了一层 ,所以名称上就把map加了个flat,叫flatmap;而他针对的也是二维数组,一对多的处理。
开发时,可以使用的idea的debug看到stream的处理过程,每一步是怎么处理的。
前言
用法区别
map自动返回stream对象;
flatmap处理后的元素依然要是stream对象(可以用stream.of,Arrays.stream将元素转为stream对象),这也是flatMap和map的用法区别,flatmap需要再加一个Arrays.stream或stream.of或者方法引用,而map不用。
简单写法
List<String> list = Arrays.asList("a1","a2","a3");
// map的用法
// 一对一的处理,在每个字符串后面加上test输出
list.stream().map(s->s+"test").forEach(System.out::println);
// 输出
a1test
a2test
a3test
// flatMap的两种用法
// 一对多的处理,把每个字符串拆成一个个字符,输出,这点map就无法做到。
ArrayList<String> list = Lists.newArrayList();
list.add("111,222");
list.add("121");
list.add("131");
List<String> collect = list.stream().flatMap(s -> Stream.of(s.split(","))).collect(Collectors.toList());
// 输出
[111,222, 121, 131]
需求
比如遇到如下需求:给List<Room>
对象,需要获取到List<String> peopleNameList
对象
// 解释:一个房间有自己的房间号,里面有一堆人,每个人都有名字
@Data
@AllArgsConstructor
@NoArgsConstructor
static class Room {
private int number;
private List<People> peopleList;
@Data
@NoArgsConstructor
@AllArgsConstructor
static class People {
private String name;
}
}
解决方案
使用Map方法
如果只是使用普通的map方法且里面是直接getPeopleList()方法,则得到的是 List<List<Room.People>> collect
对象。
public class Demo {
public static void main(String[] args) {
final List<Room> roomList = initRoom();
List<List<Room.People>> collect = roomList.stream()
.map(Room::getPeopleList)
.collect(Collectors.toList());
}
}
通常遇到这情况,一般人解决就是在遍历一次,如下:
public class Demo {
public static void main(String[] args) {
final List<Room> roomList = initRoom();
List<List<Room.People>> collect = roomList.stream()
.map(Room::getPeopleList)
.collect(Collectors.toList());
// 1.先new ArrayList<>() 准备存储String字符串
List<String> peopleNameList = new ArrayList<>();
// 2. 这里就得套两层的foreach了
// 因为初始化是List<List<T>>对象
// 第一次foreach中peopleList 是List<Room.People>对象
collect.forEach(peopleList -> {
// 第二次foreach中people 是People对象
peopleList.forEach(people -> {
peopleNameList.add(people.getName());
});
});
}
而有的人可能会在上面的基础上,再进一步优化为如下:
public class Demo {
public static void main(String[] args) {
final List<Room> roomList = initRoom();
List<String> peopleNameList = new ArrayList<>();
// 直接一直使用stream流的foreach
// 虽然是比上面代码优雅多了,但两个foreach,怎么看都不舒服
roomList.stream().forEach(room -> {
room.getPeopleList().forEach(people -> {
peopleNameList.add(people.getName());
});
});
}
}
使用flatMap
flatMap就是用于多层结构的扁平化,如下:
public class Demo {
public static void main(String[] args) {
final List<Room> roomList = initRoom();
List<String> peopleNameList = roomList.stream()
// 跟之前一样,生成多层list
.map(Room::getPeopleList)
// 直接使用flatMap,使其压缩成一个list
.flatMap(Collection::stream)
// 之后正常操作扁平化的数据即可
.map(Room.People::getName)
.collect(Collectors.toList());
}
}
完整代码
public class Demo {
public static void main(String[] args) {
final List<Room> roomList = initRoom();
// Map方法
List<String> peopleNameList = new ArrayList<>();
roomList.stream().forEach(room -> {
room.getPeopleList().forEach(people -> {
peopleNameList.add(people.getName());
});
});
// flatMap 方法
List<String> peopleNameList2 = roomList.stream()
.map(Room::getPeopleList)
.flatMap(Collection::stream)
.map(Room.People::getName)
.collect(Collectors.toList());
}
// 初始化10条数据
private static List<Room> initRoom() {
return IntStream.range(1, 11)
.mapToObj(num -> new Room(num, Arrays.asList(new Room.People("name:" + num), new Room.People("name:" + 10 * num))))
.collect(Collectors.toList());
}
@Data
@AllArgsConstructor
@NoArgsConstructor
static class Room {
private int number;
private List<People> peopleList;
@Data
@NoArgsConstructor
@AllArgsConstructor
static class People {
private String name;
}
}
}
评论区