You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Stream过滤交集时同步修改非源Product列表的name字段?

问题与解决方案

自定义类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Product {
    private String name;
    private String systemId;
    private String productId;
    private String productPartId;
    private boolean isMandatory;
    private String status;

    public Product(String name, String systemId, String productId, String productPartId) {
        this.name = name;
        this.systemId = systemId;
        this.productId = productId;
        this.productPartId = productPartId;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Product product = (Product) o;
        return Objects.equals(systemId, product.systemId) && Objects.equals(productId, product.productId)
                && Objects.equals(productPartId, product.productPartId);
    }
    @Override
    public int hashCode() {
        return Objects.hash(systemId, productId, productPartId);
    }
}

场景说明

现有两个包含Product对象的列表:

List<Product> bankConceptProducts = //已设置isMandatory和status,但未设置name
List<Product> customerProducts = //已设置name,但未设置isMandatory和status

两个列表中,equals()和hashCode()依赖的字段都已正确赋值。

当前用以下代码生成交集列表,效果符合预期:

customerProducts.stream().filter(bankConceptProducts::contains).toList()

需求

希望在过滤的同时修改bankConceptProducts中的元素(设置name字段),避免额外迭代单独赋值。能否用Stream API实现?还是必须用map(多一次迭代)或者在toList()之后处理?

注:name字段未纳入equals()和hashCode(),不会导致比较逻辑或数据不一致。
补充:已知用forEach可以实现,想确认Stream API是否可行。

以下是可用的forEach实现代码:

Map<Product, Product> bankConceptProductsMap = bankConceptsByProductId.get(customerConcept.getProductId())
        .getProducts().stream().collect(Collectors.toMap(Function.identity(), Function.identity()));

List<Product> customerConceptProducts = new ArrayList<>();

customerProducts.forEach(customerProduct -> {
    Optional<Product> matchedProduct = Optional.ofNullable(bankConceptProductsMap.get(customerProduct));
    if (matchedProduct.isPresent()) {
        matchedProduct.get().setName(customerProduct.getName());
        customerConceptProducts.add(matchedProduct.get());
    }
});

解决方案

可以用Stream API实现,核心思路是先把bankConceptProducts转成Map(和你forEach里的思路一致),然后在Stream的处理流程中完成匹配、赋值和收集,不需要额外迭代:

Map<Product, Product> bankProductMap = bankConceptProducts.stream()
        .collect(Collectors.toMap(Function.identity(), Function.identity()));

List<Product> result = customerProducts.stream()
        .map(customerProduct -> {
            Product matched = bankProductMap.get(customerProduct);
            if (matched != null) {
                matched.setName(customerProduct.getName());
            }
            return matched;
        })
        .filter(Objects::nonNull)
        .toList();

说明

  1. 先将bankConceptProducts转成Map,是为了把contains操作的O(n)时间复杂度降到O(1),比直接用filter(bankConceptProducts::contains)性能更优,尤其是列表元素较多时。
  2. 在map操作中完成匹配和name字段赋值,随后过滤掉不匹配的元素(即null),最后收集成列表。整个流程是单次Stream迭代,既完成了交集筛选,又完成了字段赋值。
  3. 由于name不影响equals和hashCode,赋值操作不会破坏Map的结构或后续的比较逻辑,完全符合你的要求。

内容的提问来源于stack exchange,提问作者Kaliyug Antagonist

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 04:52:45