Java 11嵌套for循环转Stream:构建Location后地址赋值方法疑问
将嵌套for循环的Java代码转换为Java 11 Stream实现
问题描述
需要将下面的嵌套for循环Java代码转换成Java 11 Stream实现:
private static List<Location> extractLocation(List<Location> locations){ List<Location> locationList = new ArrayList<>(); for(Location l : locations){ if(l!=null){ final Location location = Location.builder() .id(l.getId()) .build(); for(Address address: l.getAddresses()){ if(address.getType().equals("primary")){ location.setPrimaryAddress(address); } else{ location.setBillingAddress(address); } } locationList.add(location); } } return locationList; }
目前已写出部分Stream代码,但在构建完Location对象后,不清楚用哪种Stream方法处理地址赋值逻辑:
private static List<Location> extractLocationStream(List<Location> locations){ return locations.stream() .filter(location -> location!=null) .map(location -> { Location l = Location.builder() .id(location.getId()) .build(); location.getAddresses().stream() .filter(address -> address.getType().equals("primary")).// what to do now }) .collect(Collectors.toList()); }
相关实体类定义:
@Data @AllArgsConstructor class Address{ private int id; private String type; } @Data @AllArgsConstructor @Builder class Location{ private int id; private List<Address> addresses; private Address primaryAddress; private Address billingAddress; }
解决方案
你可以通过直接遍历地址完成赋值,或者先按地址类型分组再赋值,以下是两种符合需求的完整Stream实现:
方案一:基于forEach的直观实现
这种方式和原for循环逻辑完全对齐,直接遍历地址完成Builder赋值:
import java.util.Objects; import java.util.List; import java.util.stream.Collectors; private static List<Location> extractLocationStream(List<Location> locations) { return locations.stream() .filter(Objects::nonNull) .map(originalLoc -> { // 初始化Location Builder Location.LocationBuilder locBuilder = Location.builder() .id(originalLoc.getId()); // 遍历地址,给Builder设置对应字段 originalLoc.getAddresses().forEach(address -> { if ("primary".equals(address.getType())) { locBuilder.primaryAddress(address); } else { locBuilder.billingAddress(address); } }); return locBuilder.build(); }) .collect(Collectors.toList()); }
方案二:基于分组的高效实现
如果地址列表中存在多个同类型地址(比如多个primary),这种方式会保留最后一个匹配的地址(和原for循环后续地址覆盖旧值的逻辑一致):
import java.util.Map; import java.util.Objects; import java.util.List; import java.util.stream.Collectors; private static List<Location> extractLocationStream(List<Location> locations) { return locations.stream() .filter(Objects::nonNull) .map(originalLoc -> { // 按地址类型分组,保留最后一个匹配的地址 Map<String, Address> addressMap = originalLoc.getAddresses().stream() .collect(Collectors.toMap( Address::getType, addr -> addr, (oldAddr, newAddr) -> newAddr // 新地址覆盖旧地址 )); return Location.builder() .id(originalLoc.getId()) .primaryAddress(addressMap.get("primary")) .billingAddress(addressMap.getOrDefault("", addressMap.get(null))) // 匹配非primary的地址 .build(); }) .collect(Collectors.toList()); }
关键说明
- 用
Objects::nonNull方法引用替代location -> location != null,代码更简洁规范。 - 直接基于Location的Builder操作,避免先构建空对象再调用setter,更符合Builder模式的设计初衷。
- 两种方案都完全复刻原for循环的业务逻辑:过滤空Location、提取ID、按地址类型赋值对应字段。
内容的提问来源于stack exchange,提问作者nanosoft
相关产品推荐
相关产品推荐

