【JDK特性】stream 里面的map和flatMap区别
背景
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区别 (本文的例子均参考的这里,并且例子说得更好一些)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南