功能实现
1 | (A,B) |
将有关联关系的数据放在同一个集合中
1 | (A,B,E) |
方法一
通过每次添加一个数据,判断是否在集合中,并且将关联集合合并
1 | import java.util.HashSet; |
主要原理是每次添加元素,都从集合中遍历,如果出现两个set就合并set,如果只存在单个set就加入,最大的性能消耗在遍历上,set的size越大,耗时就越大。
由于业务特性,只要相关的数据在同一个set即可,5组数据在3个集合还是5个集合就不影响最后的计算结果,因此可以限定一个size大小,超过阈值进行merge操作。
方法二
优化的方式是限制Set大小,如果超过一个阈值,进行resize操作。
resize思想是,通过random的方式,判断是否需要合并数据。
通过for循环遍历所有的数据,每个数据都判断是否需要合并,如果需要,合并对应的length-i(对称)的数据,然后清空被合并的数据,可以将数据的大小压缩。
1 | import java.util.*; |
可以将耗时从半小时算不完降低到高峰期可以在1分钟多算完(50w条数据)
方法三
通过倒排索引的思想,现将每个tuple进行编号,获取一份该数据的倒排索引和正排索引。
正排索引如下1
2
3
41. (A,B)
2. (C,D)
3. (E,A)
4. (E,F)
对应的倒排索引如下
1 | A ==> 1,3 |
需要一个队列queue,放入需要被继续深究的关系。
一个集合set,放置已经被搜过的索引。
和一个存放关系的list。
在计算的时候,遍历正排索引
- 遍历索引1,获取A,B是相关关系,在list中加入A和B。在queue加入A,B,在被搜过的索引集合set中加入1,此时set中是1,queue中是A和B,list中是A和B
- 从队列中取出需要遍历的第一个数据A,获取对应的倒排索引是1和3,1已经在遍历过的索引set中,跳过,3没有,获取3中的数据E和A,E加入关系List中,E加入待深搜的队列queue中,已经被搜索的set中加入3. 此时 待深搜的queue为B和E, 已经被搜过的索引set是1和3,此时关系list是A、B和E。
- 继续弹出队列获取B,按照上一步的步骤1已经搜过,跳过。 继续弹出E,查出对应索引为3和4,3被搜过跳过,搜索4,获取F,此时可以更新关系, list中为A、B、E、F,已经搜过的索引set为1、3、4. 待深搜的队列queue为F。 弹出F,无发现。至此,第一组关系诞生,其他数据均无联系。
- 深搜2,按照上面的步骤,可以得出C和D是关系,无其他数据。
- 深搜3,发现3已经被搜过
- 深搜4同上。
- 完结。
上代码
1 | public class ClosureSet { |
跟方案二比,50w数据从1min30s,只用1.5s就可以跑完。从不完全聚合变成完全聚合。
灵感是从es来的。