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

如何实现List<Map<String,Object>>类型双数据集对账匹配?

双List<Map<String,Object>>数据集对账匹配实现

需求说明

对两个List<Map<String,Object>>类型数据集(左侧LHS=query1results、右侧RHS=query2results)执行对账匹配,需统计匹配数与断项数,规则如下:

  • 同时存在于LHS和RHS的记录为匹配项,需保留双方的匹配Map;
  • 仅存在于LHS的记录为右断项(right-hand-breaks);
  • 前两步处理后未匹配的RHS记录为左断项(left-hand-breaks);
  • 匹配判断仅基于指定key的取值,忽略其他key。

现有尝试代码

尝试1:仅实现LHS匹配,未处理断项

@Override
public void reconcile(LocalDate date) {
    List<Map<String, Object>> query1Records = executeQuery1(date).collect(Collectors.toList());
    List<Map<String, Object>> query2Records = executeQuery2(date).collect(Collectors.toList());
    
    List<Map<String, Object>> matching = query1Records.parallelStream().filter(searchData ->
            query2Records.parallelStream().anyMatch(inputMap ->
                searchData.get("instrument").equals(inputMap.get("instrument"))
                    && String.valueOf(searchData.get("entity")).equals(inputMap.get("entity"))
                    && searchData.get("party").equals(inputMap.get("party"))
                    && ((BigDecimal) searchData.get("quantity")).compareTo((BigDecimal) inputMap.get("quantity")) == 0))
        .collect(Collectors.toList());
    
}

尝试2:按指定key匹配,但未保留双方匹配记录、未处理RHS断项

List<String> keys = Arrays.asList("entity", "instrument", "party", "quantity");

Function<Map<String, Object>, List<?>> getKey = m ->
    keys.stream().map(m::get).collect(Collectors.toList());

Map<List<?>, Map<String, Object>> bpsKeys = query1Records.stream()
    .collect(Collectors.toMap(
        getKey,
        m -> m,
        (a, b) -> {
            throw new IllegalStateException("duplicate " + a + " and " + b);
        },
        LinkedHashMap::new));

List<Map<String,Object>> matchingRecords = query2Records.stream()
    .filter(m -> bpsKeys.containsKey(getKey.apply(m)))
    .collect(Collectors.toList());

matchingRecords.forEach(m -> bpsKeys.remove(getKey.apply(m)));
List<Map<String,Object>> notMatchingRecords = new ArrayList<>(bpsKeys.values());

完整解决方案

实现思路

通过指定key生成唯一匹配键,将LHS和RHS分别按匹配键构建映射表,快速查找匹配项;遍历映射表完成匹配项收集、断项分类,完整覆盖所有对账规则。

代码实现

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

// 对账结果容器类,用于存储匹配项和断项,方便统计
class ReconciliationResult {
    private List<Map<String, Object>> matchingLhsRecords;
    private List<Map<String, Object>> matchingRhsRecords;
    private List<Map<String, Object>> rightHandBreaks; // LHS独有记录
    private List<Map<String, Object>> leftHandBreaks;  // RHS独有记录

    public ReconciliationResult() {
        this.matchingLhsRecords = new ArrayList<>();
        this.matchingRhsRecords = new ArrayList<>();
        this.rightHandBreaks = new ArrayList<>();
        this.leftHandBreaks = new ArrayList<>();
    }

    // 按需添加getter方法
    public List<Map<String, Object>> getMatchingLhsRecords() { return matchingLhsRecords; }
    public List<Map<String, Object>> getMatchingRhsRecords() { return matchingRhsRecords; }
    public List<Map<String, Object>> getRightHandBreaks() { return rightHandBreaks; }
    public List<Map<String, Object>> getLeftHandBreaks() { return leftHandBreaks; }

    // 打印对账统计信息
    public void printReconciliationStats() {
        System.out.println("匹配记录数:" + matchingLhsRecords.size());
        System.out.println("右断项数(仅LHS存在):" + rightHandBreaks.size());
        System.out.println("左断项数(仅RHS存在):" + leftHandBreaks.size());
    }
}

@Override
public ReconciliationResult reconcile(LocalDate date) {
    List<Map<String, Object>> lhsRecords = executeQuery1(date).collect(Collectors.toList());
    List<Map<String, Object>> rhsRecords = executeQuery2(date).collect(Collectors.toList());

    // 定义用于匹配的key集合,忽略其他key
    List<String> matchKeys = Arrays.asList("entity", "instrument", "party", "quantity");

    // 生成匹配键的函数:统一处理key对应的取值格式,避免类型差异导致匹配失败
    Function<Map<String, Object>, List<Object>> generateMatchKey = map ->
        matchKeys.stream()
                 .map(key -> {
                     Object value = map.get(key);
                     // 统一entity为String类型,避免不同类型的相等判断问题
                     if ("entity".equals(key)) {
                         return String.valueOf(value);
                     }
                     // quantity为BigDecimal,直接使用(已重写equals和hashCode)
                     return value;
                 })
                 .collect(Collectors.toList());

    // 将LHS记录按匹配键映射,若存在重复匹配键则抛出异常(可根据业务调整)
    Map<List<Object>, Map<String, Object>> lhsKeyMap = lhsRecords.stream()
        .collect(Collectors.toMap(
            generateMatchKey,
            record -> record,
            (existing, newRecord) -> {
                throw new IllegalStateException("LHS存在重复匹配记录:" + existing);
            },
            LinkedHashMap::new
        ));

    // 将RHS记录按匹配键映射
    Map<List<Object>, Map<String, Object>> rhsKeyMap = rhsRecords.stream()
        .collect(Collectors.toMap(
            generateMatchKey,
            record -> record,
            (existing, newRecord) -> {
                throw new IllegalStateException("RHS存在重复匹配记录:" + existing);
            },
            LinkedHashMap::new
        ));

    ReconciliationResult result = new ReconciliationResult();

    // 处理匹配项和右断项
    for (Map.Entry<List<Object>, Map<String, Object>> lhsEntry : lhsKeyMap.entrySet()) {
        List<Object> matchKey = lhsEntry.getKey();
        Map<String, Object> lhsRecord = lhsEntry.getValue();

        if (rhsKeyMap.containsKey(matchKey)) {
            // 记录匹配项,同时从RHS映射中移除已匹配的键
            result.getMatchingLhsRecords().add(lhsRecord);
            result.getMatchingRhsRecords().add(rhsKeyMap.get(matchKey));
            rhsKeyMap.remove(matchKey);
        } else {
            // LHS独有,归入右断项
            result.getRightHandBreaks().add(lhsRecord);
        }
    }

    // 剩余的RHS记录全部为左断项
    result.getLeftHandBreaks().addAll(rhsKeyMap.values());

    // 输出统计结果
    result.printReconciliationStats();

    return result;
}

方案亮点

  1. 高效匹配:利用Map的O(1)查找特性,避免嵌套Stream的O(n²)时间复杂度;
  2. 规则全覆盖:完整收集双方匹配记录、LHS独有的右断项、RHS独有的左断项;
  3. 格式兼容:统一处理key对应的取值格式(如entity转String),避免因类型差异导致的匹配错误;
  4. 异常处理:检测并抛出重复匹配键的异常,避免对账结果混乱(可根据业务需求调整为合并或跳过重复记录)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 23:54:28