Java如何过滤嵌套列表?筛选不含用户过敏原的食谱列表
过敏原食谱过滤实现方案
核心判断规则:只要食谱关联的任意一个产品,包含用户过敏原列表里的任意一种过敏原,该食谱就不符合要求;只有所有产品的所有过敏原都和用户过敏原列表无交集,才会被保留。
前置注意事项
- 先确保
Alergen类正确重写了equals()和hashCode()方法,如果是根据过敏原id或者名称做唯一判定,也可以在比对时直接取对应属性判断,避免同内容不同对象导致的判断错误。 - 提前处理空值边界:比如入参为空、食谱未关联产品、产品未标注过敏原的场景,避免空指针异常。
代码实现(Java)
为了提升判断效率,先把用户传入的过敏原列表转为HashSet,把存在性判断的时间复杂度从List的O(n)降到O(1),同时利用短路判断逻辑,只要发现第一个匹配的过敏原就终止当前食谱的校验,减少无效计算。
import java.util.*; import java.util.stream.Collectors; public class RecipeFilterService { public List<Recipe> getSafeRecipes(List<Recipe> sourceRecipes, List<Alergen> userAlergens) { // 处理空入参 if (sourceRecipes == null || sourceRecipes.isEmpty()) { return Collections.emptyList(); } Set<Alergen> userAlergenSet = userAlergens == null ? Collections.emptySet() : new HashSet<>(userAlergens); // 用户无过敏原时直接返回全部食谱 if (userAlergenSet.isEmpty()) { return sourceRecipes; } return sourceRecipes.stream() .filter(recipe -> isRecipeSafeForUser(recipe, userAlergenSet)) .collect(Collectors.toList()); } private boolean isRecipeSafeForUser(Recipe recipe, Set<Alergen> userAlergenSet) { List<Product> products = recipe.getProducts(); // 未关联产品的食谱默认安全 if (products == null || products.isEmpty()) { return true; } // 遍历所有产品,只要发现一个匹配过敏原就直接返回不安全 for (Product product : products) { List<Alergen> productAlergens = product.getAlergens(); if (productAlergens == null || productAlergens.isEmpty()) { continue; } for (Alergen alergen : productAlergens) { if (userAlergenSet.contains(alergen)) { return false; } } } return true; } }
如果偏好更简洁的Stream写法,可以把判断逻辑直接用noneMatch实现,效果完全一致,noneMatch本身自带短路判断逻辑:
// 替换上面filter里的方法引用即可 .filter(recipe -> { List<Product> products = recipe.getProducts(); if (products == null || products.isEmpty()) { return true; } return products.stream() .map(Product::getAlergens) .filter(Objects::nonNull) .flatMap(List::stream) .noneMatch(userAlergenSet::contains); })
逻辑说明
- 所有空场景都做了兼容,不会因为缺字段抛出空指针
- 用
HashSet存储用户过敏原,大数据量下判断性能远高于直接遍历List - 短路判断逻辑避免遍历不必要的数据,执行效率更高
内容的提问来源于stack exchange,提问作者bkprog
相关产品推荐
相关产品推荐

