比对两个列表,找出新增、修改、删除的部分

69 阅读1分钟

场景

  • 每个账户下有多个币种子账户,属于1:N的关系。
  • 每个币种子账户都有自己的余额、状态。
  • 运营在后台可批量新增、启用、关闭某个账户的币种子账户。
  • 因此需要比对新、旧记录,找出新增、修改、删除的部分,并进行更新。

比对工具类

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;

public final class CompareUtil {
    private CompareUtil() {}

    /**
     * 比对两个列表,找出新增、修改、删除的部分
     *
     * @param newRecords  新记录
     * @param oldRecords  旧记录
     * @param keyMapper   比对键映射方法
     * @param updateMerge 修改合并方法
     * @param <T>         记录实体类型
     * @param <K>         比对键类型
     * @return 比对结果
     */
    public static <T, K> Result<T> compare(List<T> newRecords,
                                           List<T> oldRecords,
                                           Function<T, K> keyMapper,
                                           BiConsumer<T, T> updateMerge) {
        List<T> addList = new ArrayList<>();
        List<T> updateList = new ArrayList<>();
        List<T> deleteList = new ArrayList<>();

        Map<K, T> newRecordMap = listToMap(newRecords, keyMapper);
        Map<K, T> oldRecordMap = listToMap(oldRecords, keyMapper);

        for (Map.Entry<K, T> entry : newRecordMap.entrySet()) {
            T oldRecord = oldRecordMap.get(entry.getKey());
            if (oldRecord == null) {
                addList.add(entry.getValue());
            } else {
                updateMerge.accept(entry.getValue(), oldRecord);
                updateList.add(oldRecord);
            }
        }

        for (Map.Entry<K, T> entry : oldRecordMap.entrySet()) {
            if (!newRecordMap.containsKey(entry.getKey())) {
                deleteList.add(entry.getValue());
            }
        }

        return new Result<>(addList, updateList, deleteList);
    }

    private static <T, K> Map<K, T> listToMap(List<T> list, Function<T, K> keyMapper) {
        Map<K, T> map = new HashMap<>(list.size());
        for (T t : list) {
            map.put(keyMapper.apply(t), t);
        }
        return map;
    }

    public record Result<T>(List<T> addList, List<T> updateList, List<T> deleteList) {}

}

使用示例

实体类

public class AccountCurrency {

    // 账户id
    // 余额

    /**
     * 币种
     */
    private String currency;

    /**
     * 是否启用
     */
    private boolean enabled;

    public AccountCurrency(String currency, boolean enabled) {
        this.currency = currency;
        this.enabled = enabled;
    }

    public String getCurrency() {
        return currency;
    }

    public void setCurrency(String currency) {
        this.currency = currency;
    }

    public boolean isEnabled() {
        return enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    @Override
    public String toString() {
        return "{" +
            "currency='" + currency + '\'' +
            ", enabled=" + enabled +
            '}';
    }

}

模拟测试

import java.util.ArrayList;
import java.util.List;

public class CompareTest {

    public static void main(String[] args) {
        List<AccountCurrency> newRecords = new ArrayList<>();
        newRecords.add(new AccountCurrency("1", true));
        newRecords.add(new AccountCurrency("4", true));

        List<AccountCurrency> oldRecords = new ArrayList<>();
        oldRecords.add(new AccountCurrency("1", false));
        oldRecords.add(new AccountCurrency("2", true));
        oldRecords.add(new AccountCurrency("3", true));
        
        // 预期结果:
        // 新增 4
        // 修改 1
        // 删除 2、3

        CompareUtil.Result<AccountCurrency> result = CompareUtil.compare(
            newRecords,
            oldRecords,
            AccountCurrency::getCurrency,
            (newRecord, oldRecord) -> {
                oldRecord.setEnabled(newRecord.isEnabled());
            }
        );

        System.out.println("addList: " + result.addList());
        System.out.println("updateList: " + result.updateList());
        System.out.println("deleteList: " + result.deleteList());
    }

}

输出结果

addList: [{currency='4', enabled=true}]
updateList: [{currency='1', enabled=true}]
deleteList: [{currency='2', enabled=true}, {currency='3', enabled=true}]

Process finished with exit code 0