用遞迴演算法得到Java的樹形結構
阿新 • • 發佈:2019-02-06
要求:得到無限分類的選單欄。並且告訴你任何一個節點,都能得到整個選單。
資料庫是mongodb。沒有貼全部,只貼部分。
首先一個整體思路是:
1、要有一個能通過父類id得到全部子類id的集合。
2、遍歷這些子類集合來把它們的關係關聯起來。
程式碼部分:
建立資料庫的查詢:
DBCollection collection = GGMongoOperator.getGGBusinessCollection("gg_help_center_v3");//幫助中心表
DBObject query = new BasicDBObject("status",1);
DBObject object = new BasicDBObject("_id",1).append("name", 1).append("pId", 1).append("icon", 1);
DBCursor cursor = collection.find(query, object).sort(new BasicDBObject("add_time",1));
List<Map<String,Object>> nodeList = GGDBCursor.find(cursor);
其中List<Map<String,Object>> nodeList = GGDBCursor.find(cursor);
是我公司自己封裝的方法。不使用的話,就是用while迴圈自己來組裝資料。
得到的資料格式 key value;比如:
map.put(“_id”,1);
map.put(“name”,”每日頭條”)
list.add(map);
…
之後就是為思路1做準備。緊接著上面的程式碼:
Map<String,Tree> treeMap = new HashMap<String, Tree>();
Map<String, List<String>> pidMap = new HashMap<String, List<String >>();
for (Map<String, Object> map : nodeList) {
//1、我要拿到頂層節點
Object pId = map.get("pId");
String pid = null;
if(pId != null){
pid = pId.toString();
}else{
continue;
}
//2、通過父類id能拿到全部子類的id
if(pidMap.get(pid) == null){
List<String> list = new ArrayList<String>();
list.add(map.get("_id").toString());
pidMap.put(pId.toString(), list);
}else{
List<String> list = pidMap.get(pId.toString());
list.add(map.get("_id").toString());
}
Tree t = new Tree();
t.setParentId(map.get("pId").toString());
t.setName(map.get("name").toString());
t.setId(map.get("_id").toString());
treeMap.put(map.get("_id").toString(), t);
}
List<String> fatherId = new ArrayList<String>(pidMap.keySet());
fatherId.remove("0"); //0代表的是頂級目錄的pid,我後面遍歷fatherId獲得相應的實體類,但是頂級pid是沒有相應的實體類的,所以要移除,否則會報null
int size = fatherId.size();
int ii = 0; //我用來做測試,迴圈多少次
return getTree(fatherId, nodeList.get(0).get("_id").toString(), treeMap, pidMap, new ArrayList<Tree>(), size, ii);
上面的程式碼中pidMap就是思路1中所說的集合,通過父類id,得到全部的子類id。
treeMap:儲存的就是所有資料的實體類。格式是:key:_id, value:實體類。
Tree:這個是我自己定義的pojo類。
屬性有:id,name,parentId,List<Tree> childrenList = new ArrayList<
Tree>();
getTree(…)這個方法就是思路2中去遍歷各個子類集合中把它們的關係關聯起來。
程式碼如下:
private static List<Tree> getTree(List<String> fatherId, String id, Map<String,Tree> treeMap, Map<String, List<String>> pidMap, List<Tree> result,int size,int ii) {
//先去獲取當前物件,在把子節點的treeset到children裡面去。
Tree tree = treeMap.get(id); //這個tree就是實體類!
if(tree !=null){ //正常情況下不會為空,出現為空的情況都是因為有髒資料造成的
if(tree.getParentId() == null||"0".equals(tree.getParentId())){
//我這裡應該儲存的是一級物件,子物件都在一級物件裡面
if(!result.contains(tree)){
result.add(tree);
}
}
//若是葉子節點就沒有必要走下面的遍歷
List<String> childList = pidMap.get(id);
if(childList != null){
//以下遍歷是為了找出子節點
for(String c : childList){
Tree childTree = treeMap.get(c);
String pid = childTree.getParentId();
if(id.equals(pid)){
List<Tree> childrenList = tree.getChildrenList();
if(!childrenList.contains(childTree)){
childrenList.add(childTree);
}
}
ii++;
}
}
}
if(size <= 0){//結束
return result;
}
int ss = size-1;
result = getTree(fatherId, fatherId.get(ss), treeMap, pidMap, result, ss,ii);
System.out.println("迴圈了======================" + ii);
return result;
}
詳解————這裡我使用了遞迴:
if(tree.getParentId() == null||"0".equals(tree.getParentId())){
//我這裡應該儲存的是一級物件,子物件都在一級物件childrenList裡面
if(!result.contains(tree)){
result.add(tree);
}
}
這段程式碼的意思就是當它是頂級節點時就加入result。我這麼寫是假設它有多個頂級節點。一般情況下就一個。↑
//若是葉子節點就沒有必要走下面的遍歷
List<String> childList = pidMap.get(id);
if(childList != null){
//以下遍歷是為了找出子節點
for(String c : childList){
Tree childTree = treeMap.get(c);
String pid = childTree.getParentId();
if(id.equals(pid)){
List<Tree> childrenList = tree.getChildrenList();
if(!childrenList.contains(childTree)){
childrenList.add(childTree);
}
}
ii++;
}
}
這個遍歷就是去遍歷子類節點集合把它們都新增到父類的childrenList中去。↑
if(size <= 0){//結束
return result;
}
int ss = size-1;
result = getTree(fatherId, fatherId.get(ss), treeMap, pidMap, result, ss,ii);
System.out.println("迴圈了======================" + ii);
return result;
這塊程式碼是進行遞迴的關鍵。每次遞迴時,我都從fatherId中取一個id。
fatherId儲存的是所有具有子類的id集合,也就是說,葉子節點不在裡面,在裡面的都是有子類的;↑
還有個要注意的地方:
if(!childrenList.contains(childTree)){
childrenList.add(childTree);
}
這裡使用contains是因為childTree是個list陣列,不會去重。若不去重,就會造成重複資料。↑
自此關鍵程式碼就結束啦!
public class Tree {
/**
* 節點唯一標識
*/
private String id;
/**
* 節點名稱
*/
private String name;
/**
* 所屬父節點ID
*/
private String parentId;
/**
* 所屬父節點物件
*/
//private Tree parentObj;
/**
* 所含子節點
*/
private List<Tree> childrenList = new ArrayList<Tree>();
就不詳細列出來。。。
完整的程式碼:
public static List<Tree> getAllNodes2(){
DBCollection collection = GGMongoOperator.getGGBusinessCollection("gg_help_center_v3");//幫助中心表
DBObject query = new BasicDBObject("status",1);
DBObject object = new BasicDBObject("_id",1).append("name", 1).append("pId", 1).append("icon", 1);
DBCursor cursor = collection.find(query, object).sort(new BasicDBObject("add_time",1));
List<Map<String,Object>> nodeList = GGDBCursor.find(cursor);
Map<String,Tree> treeMap = new HashMap<String, Tree>();
Map<String, List<String>> pidMap = new HashMap<String, List<String>>();
for (Map<String, Object> map : nodeList) {
//1、我要拿到頂層節點
Object pId = map.get("pId");
String pid = null;
if(pId != null){
pid = pId.toString();
}else{
continue;
}
//2、通過父類id能拿到全部子類的id
if(pidMap.get(pid) == null){
List<String> list = new ArrayList<String>();
list.add(map.get("_id").toString());
pidMap.put(pId.toString(), list);
}else{
List<String> list = pidMap.get(pId.toString());
list.add(map.get("_id").toString());
}
Tree t = new Tree();
t.setParentId(map.get("pId").toString());
t.setName(map.get("name").toString());
t.setId(map.get("_id").toString());
treeMap.put(map.get("_id").toString(), t);
}
List<String> fatherId = new ArrayList<String>(pidMap.keySet());
fatherId.remove("0");
int size = fatherId.size();
int ii = 0;
return getTree(fatherId, nodeList.get(0).get("_id").toString(), treeMap, pidMap, new ArrayList<Tree>(), size, ii);
}
private static List<Tree> getTree(List<String> fatherId, String id, Map<String,Tree> treeMap, Map<String, List<String>> pidMap, List<Tree> result,int size,int ii) {
//先去獲取當前物件,在把子節點的treeset到children裡面去。
Tree tree = treeMap.get(id); //這個tree就是那個固定的常量!
if(tree !=null){
if(tree.getParentId() == null||"0".equals(tree.getParentId())){
//我這裡應該儲存的是一級物件,子物件都在一級物件裡面
if(!result.contains(tree)){
result.add(tree);
}
}
//若是葉子節點就沒有必要走下面的遍歷
List<String> childList = pidMap.get(id);
if(childList != null){
//以下遍歷是為了找出子節點
for(String c : childList){
Tree childTree = treeMap.get(c);
String pid = childTree.getParentId();
if(id.equals(pid)){
List<Tree> childrenList = tree.getChildrenList();
if(!childrenList.contains(childTree)){
childrenList.add(childTree);
}
}
ii++;
}
}
}
if(size <= 0){//結束
return result;
}
int ss = size-1;
result = getTree(fatherId, fatherId.get(ss), treeMap, pidMap, result, ss,ii);
System.out.println("迴圈了======================" + ii);
return result;
}
再加上Tree這個pojo類,就OK啦!