有一種數據,例如組織結構,在批量添加到系統中時,需要先從根節點開始添加,然後是第二層組織、第三層組織…而批量添加的數據有時並不是有序的,這就需要對數據做一個排序的預處理,這樣的排序我把它稱爲按層次排序。
- 背景
有一些組織的數據,可能是從csv等文本中導入的,也可能是從第三方系統中同步的,且數據量可能會很大,我們需要對這樣的數據做層次排序後批量插入到我方的數據庫中。還有一種特殊情況,就是可能數據已經同步過一次了,到第二次同步時已經不再是一個完整的組織了。 - 解決思路
(1)原始數據中將父節點相同的放在一起
(2)按照深度優先的原則,從第一個父節點開始構造組織樹,找以它的子節點爲父節點的數據,直到將第一個父節點下所有的子孫節點都找到爲止,已經遍歷過的數據做刪除標記,下次不再處理
(3)從第二個父節點開始,重複步驟(2),考慮結果是多顆子樹的情況
(4)使用隊列對結果做樹的層次遍歷,目的就是將結果再轉換回一條一條的組織數據,而不是樹,方便做批量插入。 - 實現
(1)原始數據中將父節點相同的放在一起
/**
* 將原始數據中pid相同的放在一起
*
* @param originalData
* @return
*/
public List<OrgOriginalDto> orgDataDeal(List<OrgOriginalDto> originalData){
List<OrgTreeNode> nodesList = parseNode(originalData);
if(ObjectUtils.isEmpty(nodesList)){
return null;
}
Map<String, MidDataDto> map = new HashMap<>(16);
nodesList.forEach(item->{
if(map.containsKey(item.getPid())){
map.get(item.getPid()).getData().add(item);
}else{
MidDataDto midDataDto = new MidDataDto();
List<OrgTreeNode> children = new ArrayList<>();
children.add(item);
midDataDto.setData(children);
map.put(item.getPid(), midDataDto);
}
});
buildTree(map);
List<OrgOriginalDto> result = getLevelList(map);
return result;
}
(2)按深度優先構造組織樹
private void buildTree(Map<String, MidDataDto> data){
if(!ObjectUtils.isEmpty(data)){
data.forEach((k,v)->{
if(!v.isDelete()){
findChildrenByRecursion(v, data);
}
});
}
}
private void findChildrenByRecursion(MidDataDto source, Map<String, MidDataDto> data){
if(!source.isDelete() && !ObjectUtils.isEmpty(source.getData())){
source.getData().forEach(item->{
String nextLevelPid = item.getId();
if (data.containsKey(nextLevelPid)) {
MidDataDto midDataDto = data.get(nextLevelPid);
item.setChildren(midDataDto.getData());
if(!midDataDto.isDelete()){
findChildrenByRecursion(midDataDto, data);
}
midDataDto.setDelete(true);
}
});
}
}
(3)層次遍歷法再將樹平鋪成list
/**
* 將樹再平鋪成list,使用樹的層次遍歷法
*
* @param data
* @return
*/
private List<OrgOriginalDto> getLevelList(Map<String, MidDataDto> data){
List<OrgOriginalDto> result = new ArrayList<>();
Queue<OrgTreeNode> queue = new LinkedList<>();
data.forEach((k,v)->{
if(!v.isDelete()){
v.getData().forEach(item->{
result.add(parseOrgDto(item));
queue.offer(item);
});
treeTraverseByLevel(result, queue);
}
});
return result;
}
private void treeTraverseByLevel(List<OrgOriginalDto> list, Queue<OrgTreeNode> queue){
while(!queue.isEmpty()){
OrgTreeNode orgTreeNode = queue.poll();
if(!ObjectUtils.isEmpty(orgTreeNode.getChildren())){
orgTreeNode.getChildren().forEach(item->{
list.add(parseOrgDto(item));
queue.offer(item);
});
}
}
}
最後,模擬數據測試
@Component
public class TestInit implements CommandLineRunner {
@Autowired
private OriginalDataHandler originalDataHandler;
@Override
public void run(String... strings) throws Exception {
List<OrgOriginalDto> data = new ArrayList<>();
OrgOriginalDto o11 = new OrgOriginalDto();
o11.setId("4");
o11.setPid("2");
o11.setOrgName("org11");
data.add(o11);
OrgOriginalDto o1 = new OrgOriginalDto();
o1.setId("2");
o1.setPid("1");
o1.setOrgName("org1");
data.add(o1);
OrgOriginalDto o2 = new OrgOriginalDto();
o2.setId("3");
o2.setPid("1");
o2.setOrgName("org2");
data.add(o2);
OrgOriginalDto o0 = new OrgOriginalDto();
o0.setId("1");
o0.setPid("-1");
o0.setOrgName("root");
data.add(o0);
//對原始數據進行重新排序
List<OrgOriginalDto> orgList = originalDataHandler.orgDataDeal(data);
soutList(orgList);
//批量插入
}
private void soutList(List<OrgOriginalDto> list){
if(!ObjectUtils.isEmpty(list)){
list.forEach(item->{
System.out.println(item);
});
}
}
}