背景
JDK8中, stream流程 下的map 就不多说了,很熟悉,flatmap倒是不是特别熟悉,
map 和 flatMap 的区别
看一个例子:
List<String> cityListOne = new ArrayList<>();
cityListOne.add("郑州");
cityListOne.add("濮阳");
List<String> cityListTwo = new ArrayList<>();
cityListTwo.add("廊坊");
cityListTwo.add("邢台");
List<String> cityListThree = new ArrayList<>();
cityListThree.add("大同");
cityListThree.add("太原");
List<String> cityListFour = new ArrayList<>();
cityListFour.add("南昌");
cityListFour.add("九江");
Address addressOne = new Address();
addressOne.setProvince("河南");
addressOne.setCityList(cityListOne);
Address addressTwo = new Address();
addressTwo.setProvince("河北");
addressTwo.setCityList(cityListTwo);
Address addressThree = new Address();
addressThree.setProvince("山西");
addressThree.setCityList(cityListThree);
Address addressFour = new Address();
addressFour.setProvince("江西");
addressFour.setCityList(cityListFour);
List<Address> addresses = new ArrayList<>();
addresses.add(addressOne);
addresses.add(addressTwo);
addresses.add(addressThree);
addresses.add(addressFour);
//使用map输出所有的城市名称
addresses.stream()
.map(address -> address.getCityList())
.forEach(cityList ->
cityList.forEach(city -> System.out.print(city + " "))
);
System.out.println("");
//使用flatMap输出所有城市名称
addresses.stream()
.flatMap(address -> address.getCityList().stream())
.forEach(city -> System.out.print(city + " "));
}
这里可以看到使用 flatMap 也是返回一个 stream流
看到源码也知一二,看到 map 和 flatmap 对于里面的函数式的差异:
@Override
@SuppressWarnings("unchecked")
public final <R> Stream<R> map(Function<? super P_OUT, ? extends R> mapper) {
Objects.requireNonNull(mapper);
return new StatelessOp<P_OUT, R>(this, StreamShape.REFERENCE,
StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) {
@Override
Sink<P_OUT> opWrapSink(int flags, Sink<R> sink) {
return new Sink.ChainedReference<P_OUT, R>(sink) {
@Override
public void accept(P_OUT u) {
downstream.accept(mapper.apply(u));
}
};
}
};
}
@Override
public final <R> Stream<R> flatMap(Function<? super P_OUT, ? extends Stream<? extends R>> mapper) {
Objects.requireNonNull(mapper);
// We can do better than this, by polling cancellationRequested when stream is infinite
return new StatelessOp<P_OUT, R>(this, StreamShape.REFERENCE,
StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT | StreamOpFlag.NOT_SIZED) {
@Override
Sink<P_OUT> opWrapSink(int flags, Sink<R> sink) {
return new Sink.ChainedReference<P_OUT, R>(sink) {
@Override
public void begin(long size) {
downstream.begin(-1);
}
@Override
public void accept(P_OUT u) {
try (Stream<? extends R> result = mapper.apply(u)) {
// We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it
if (result != null)
result.sequential().forEach(downstream);
}
}
};
}
};
}
嗯,怎么说呢。flalmap 还可以:使用多次
public class User {
private String name;
private List<Address> addressList;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Address> getAddressList() {
return addressList;
}
public void setAddressList(List<Address> addressList) {
this.addressList = addressList;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", addressList=" + addressList +
'}';
}
}
// 测试类
public static void main(String[] args) {
List<String> cityListOne = new ArrayList<>();
cityListOne.add("郑州");
cityListOne.add("濮阳");
List<String> cityListTwo = new ArrayList<>();
cityListTwo.add("廊坊");
cityListTwo.add("邢台");
List<String> cityListThree = new ArrayList<>();
cityListThree.add("大同");
cityListThree.add("太原");
List<String> cityListFour = new ArrayList<>();
cityListFour.add("南昌");
cityListFour.add("九江");
Address addressOne = new Address();
addressOne.setProvince("河南");
addressOne.setCityList(cityListOne);
Address addressTwo = new Address();
addressTwo.setProvince("河北");
addressTwo.setCityList(cityListTwo);
Address addressThree = new Address();
addressThree.setProvince("山西");
addressThree.setCityList(cityListThree);
Address addressFour = new Address();
addressFour.setProvince("江西");
addressFour.setCityList(cityListFour);
List<Address> addresseListOne = new ArrayList<>();
addresseListOne.add(addressOne);
addresseListOne.add(addressTwo);
List<Address> addresseListTwo = new ArrayList<>();
addresseListTwo.add(addressThree);
addresseListTwo.add(addressFour);
//新增用户来包含地址集合
List<User> userList = new ArrayList<>();
User u1 = new User();
u1.setName("张三");
u1.setAddressList(addresseListOne);
User u2 = new User();
u2.setName("李四");
u2.setAddressList(addresseListTwo);
userList.add(u1);
userList.add(u2);
//使用map输出所有的城市名称
userList.stream()
.map(u->u.getAddressList())
.forEach(addressList->{
addressList.forEach(address -> {
address.getCityList().forEach(city->{
System.out.print(city);
});
});
});
System.out.println("");//换行
//使用flatMap输出所有城市名称
userList.stream()
.flatMap(u -> u.getAddressList().stream())
.flatMap(a -> a.getCityList().stream())
.forEach(city -> System.out.print(city));
}
可以看出flatMap可以使用多次将更深一层的集合流中的数据拿到外层进行处理。而使用map则相当繁琐
就此,如果再遇遍历,可多尝试用一下 flapMap。
完。
参考:
java8中map和flatMap区别 (本文的例子均参考的这里,并且例子说得更好一些)