集合对象纵向維度聚合

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

集合纵向聚合

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

逻辑抽象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 进行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;
}
测试示例
1
2
3
4
5
6
7
8
9
10
11
@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)));
}
执行结果
1
根据Key值聚合结果:{"L1":[1,2,3,3,4,5],"L2":[1,1,2,2]}
对象纵项聚合

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

纵向装配方案定义
  1. 定义对象唯一规范
1
2
3
4
5
6
7
8
9
public interface Atomicity {

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

}
  1. 定义可纵向装配行为
1
2
3
4
5
6
7
8
public interface Assemble <D extends Atomicity> extends Atomicity {

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

}
  1. 定义纵向装配逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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. 对象实现纵向装配行为
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
@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. 对象纵向装配行为测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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. 执行结果
1
2
3
4
目标集合为空:[]
追加集合为空:[{"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"}]

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