对象聚合 - 容器对象纵向聚合

257 阅读3分钟

业务开发中经常会遇到列项聚合的问题,所以常用的一些思路整理出来.

集合纵向聚合

系统中可能存在不同的业务流程聚合出来的(K,V)结构数据, 然后需要汇总, 可以采用Java8进行流式处理

逻辑抽象
/**
 * 进行map集合中相同元素的value值聚合
 * @param map1 基础map
 * @param map2 扩展map
 * @return
 */
public static <K, V> Map<K, List<V>> keyMerge(Map<K, List<V>> map1, Map<K, List<V>> map2){
    Map<K, List<V>> base = Optional.ofNullable(map1).orElse(Maps.newHashMap());
    Map<K, List<V>> ext = Optional.ofNullable(map2).orElse(Maps.newHashMap());
    ext.keySet().forEach(
            key -> base.merge(key, ext.get(key), (v1,v2)
                    -> Arrays.asList(v1, v2).stream().flatMap(Collection::stream).collect(Collectors.toList())
            )
    );
    return base;
}
测试示例
@Test
public void keyMerge() {
    Map<String,List<Integer>> m1 = Maps.newHashMap();
    m1.put("L1", Arrays.asList(1, 2, 3));

    Map<String,List<Integer>> m2 = Maps.newHashMap();
    m2.put("L1", Arrays.asList(3, 4, 5));
    m2.put("L2", Arrays.asList(1, 1, 2, 2));

    System.out.println("根据Key值聚合结果:" + JSON.toJSONString(keyMerge(m1, m2)));
}
执行结果
根据Key值聚合结果:{"L1":[1,2,3,3,4,5],"L2":[1,1,2,2]}
对象纵项聚合

我们的业务系统可能纵向扩好几个表,但是各个表之间又有纵向key关联, 为了不进行太多JOIN方式的聚合, 异步可以进行单维度查询,然后最后结果并行聚合

纵向装配方案定义
  1. 定义对象唯一规范
public interface Atomicity {

    /**
     * 唯一标识定义
     * @return
     */
    String uniquely();

}
  1. 定义可纵向装配行为
public interface Assemble <D extends Atomicity> extends Atomicity {

    /**
     * 可纵向装配操作
     */
    D assemble(D d);

}
  1. 定义纵向装配逻辑
public class Assembly {

    private Assembly(){}

    /**
     * 列项追加逻辑,将assemblyList上的元素追加到baseDataList上,
     * 如果baseDataList为空,则返回空集合。
     * 如果待追加的元素不存在于目标集合中则忽略
     * @param baseDataList 目标集合元素
     * @param assembleList 追加集合元素
     * @param <K>
     * @param <V>
     * @return 返回目标集合中填充后的元素对象
     */
    public static <K extends Atomicity, V extends Assemble<K>> List<K> assem(List<K> baseDataList, List<V> assembleList) {

        Map<String, K> baseMap = Optional.ofNullable(baseDataList).orElse(Lists.newArrayList())
                .stream().collect(Collectors.toMap(K::uniquely, Function.identity()));

        Optional.ofNullable(assembleList).orElse(Lists.newArrayList())
                .stream().forEach(x ->{
                    K k = baseMap.get(x.uniquely());
                    if(null != k) {
                        x.assemble(k);
                    }
                }
        );
        return baseDataList;
    }

}
纵向装配使用示例
  1. 对象实现纵向装配行为
@Data
static class AssemMock implements Assemble<AssemMock>{

    private Integer id;
    private String name;
    private Integer age;

    public AssemMock() {

    }

    public AssemMock(Integer id) {
        this.id = id;
    }

    public AssemMock(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public AssemMock(Integer id, Integer age) {
        this.id = id;
        this.age = age;
    }

    public AssemMock(Integer id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    /**
     * 定义填充依赖的唯一因子
     * @return
     */
    @Override
    public String uniquely() {
        return String.valueOf(id);
    }

    /**
     * 定义填充依赖的附属逻辑
     * @param assemMock
     * @return
     */
    @Override
    public AssemMock assemble(AssemMock assemMock) {
        if(null != getId()){
            assemMock.setId(getId());
        }
        if(null != getName()){
            assemMock.setName(getName());
        }
        if(null != getAge()){
            assemMock.setAge(getAge());
        }
        return assemMock;
    }

    @Override
    public String toString(){
        return JSON.toJSONString(this);
    }

}
  1. 对象纵向装配行为测试
public static void main(String[] args) {

    List<AssemMock> targets = Arrays.asList(
            new AssemMock(1), new AssemMock(2), new AssemMock(3)
    );

    List<AssemMock> assem1 = Arrays.asList(
            new AssemMock(1, 21), new AssemMock(2, 20), new AssemMock(3, 19), new AssemMock(4, 22)
    );

    List<AssemMock> assem2 = Arrays.asList(
            new AssemMock(1, "dennisit"), new AssemMock(2, "elonsu"), new AssemMock(3, "gosling"), new AssemMock(4, "andi")
    );

    System.out.println("目标集合为空:" + Assembly.assem(Lists.newArrayList(), targets));


    System.out.println("追加集合为空:" + Assembly.assem(targets, Lists.newArrayList()));

    // 因为目标集合中不存在唯一属性为4的对象, 所以待追加集合中的属性为4的对象被忽略
    System.out.println("列项追加对象:" + Assembly.assem(targets, assem1));

    System.out.println("列项追加对象:" + Assembly.assem(targets, assem2));

}
  1. 执行结果
目标集合为空:[]
追加集合为空:[{"id":1}, {"id":2}, {"id":3}]
列项追加对象:[{"age":21,"id":1}, {"age":20,"id":2}, {"age":19,"id":3}]
列项追加对象:[{"age":21,"id":1,"name":"dennisit"}, {"age":20,"id":2,"name":"elonsu"}, {"age":19,"id":3,"name":"gosling"}]

可以看到,数据根据我们定义的唯一性规范进行了列项装配.实际开发中对于不同的列数据可以进行异步处理. 然后展示的时候聚合异步处理的结果