基于子对象属性在Java中查找嵌套对象的优化方案咨询
嘿,我完全懂你这种被多层嵌套循环折磨的感受——不仅代码写起来繁琐,后续看的时候也得一层层扒逻辑,太闹心了!下面给你两种更高效优雅的实现方式,分别用Java原生的Stream API(不用额外依赖)和Hamcrest类库,都能完美替代你的嵌套循环。
方案一:Java原生Stream API实现(推荐)
Java 8及以上自带的Stream API非常适合处理这种嵌套集合的查找,用flatMap可以逐层展开嵌套的集合,再通过anyMatch判断是否存在匹配的条件,代码更简洁且可读性更强:
private HotelResult findHotelResult(List<HotelResult> hotelResultsList, HotelSelection hotelSelection) { // 先把目标ID抽出来,避免重复调用方法 String targetResultId = hotelSelection.getResultId(); return hotelResultsList.stream() // 过滤出包含目标RoomRateId的HotelResult .filter(hotelResult -> hotelResult.getRoomOptions().stream() // 展开RoomOption集合为RoomTypeIds流 .flatMap(roomOption -> roomOption.getRoomTypeIds().stream()) // 再展开RoomTypeIds集合为RoomRateIds流 .flatMap(roomTypeIds -> roomTypeIds.getRoomRateIds().stream()) // 检查是否有RoomRateId匹配目标ID .anyMatch(roomRateIds -> roomRateIds.getId().equals(targetResultId)) ) // 取第一个符合条件的对象 .findFirst() // 如果没找到,返回null(也可以根据需求改成抛出异常或返回默认对象) .orElse(null); }
代码解释:
stream():把集合转换成流,方便后续链式操作flatMap():把嵌套的集合“扁平化”成单层流,比如把每个RoomOption里的RoomTypeIds集合都展开到同一个流里anyMatch():只要流里有一个元素符合条件(ID匹配),就返回truefindFirst():获取流中第一个符合条件的元素,返回Optional<HotelResult>orElse(null):如果Optional为空(没找到匹配对象),就返回null
方案二:Hamcrest类库实现
如果你更倾向于声明式的写法(代码读起来像自然语言),可以用Hamcrest的匹配器来实现。首先需要引入Hamcrest的依赖(以Maven为例):
<dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-library</artifactId> <version>2.2</version> <!-- 如果是生产环境使用,把scope改成compile --> <scope>test</scope> </dependency>
然后用Hamcrest的嵌套匹配器来实现查找:
import static org.hamcrest.Matchers.*; private HotelResult findHotelResult(List<HotelResult> hotelResultsList, HotelSelection hotelSelection) { String targetResultId = hotelSelection.getResultId(); return hotelResultsList.stream() .filter(hotelResult -> // 检查HotelResult的RoomOptions中是否存在符合条件的嵌套属性 hasItem( hasProperty("roomTypeIds", hasItem( hasProperty("roomRateIds", hasItem( hasProperty("id", equalTo(targetResultId)) )) )) ).matches(hotelResult.getRoomOptions()) ) .findFirst() .orElse(null); }
代码解释:
hasItem():判断集合中是否存在符合条件的元素hasProperty():判断对象是否包含指定名称的属性,并验证该属性的值equalTo():判断属性值是否等于目标ID- 整个匹配逻辑读起来就像:“RoomOptions集合中是否存在一个元素,它的roomTypeIds集合里存在一个元素,其roomRateIds集合里存在一个id等于目标ID的元素”
额外优化建议
如果你的代码中经常需要做这种嵌套属性的匹配,可以把判断逻辑抽成一个单独的方法,提高代码复用性和可读性:
// 抽离的匹配方法 private boolean isHotelMatchingTargetId(HotelResult hotelResult, String targetId) { return hotelResult.getRoomOptions().stream() .flatMap(roomOption -> roomOption.getRoomTypeIds().stream()) .flatMap(roomTypeIds -> roomTypeIds.getRoomRateIds().stream()) .anyMatch(roomRateIds -> roomRateIds.getId().equals(targetId)); } // 简化后的查找方法 private HotelResult findHotelResult(List<HotelResult> hotelResultsList, HotelSelection hotelSelection) { String targetResultId = hotelSelection.getResultId(); return hotelResultsList.stream() .filter(hotel -> isHotelMatchingTargetId(hotel, targetResultId)) .findFirst() .orElse(null); }
这样后续如果匹配逻辑需要修改,只需要改动抽离的方法即可,非常方便。
内容的提问来源于stack exchange,提问作者Rodrigo Vaamonde
相关产品推荐
相关产品推荐

