1. 程式人生 > >第七篇:遞迴插入,修改版

第七篇:遞迴插入,修改版

專案場景:
        將一個樹上的節點插入到資料庫,節點之間有父子關係,每個節點有一個id和pId,父子關係表述為:父節點的id為子節點的pId值。在上一個日誌中,所有節點的值已經全部打包完傳給了後臺。現在執行插入操作。
難點:
        舉個例,A是根節點,他有一個treeid,姑且叫Aid,他的父id為null。插入資料庫後會生成一個新的id,姑且叫NewAid,
以Aid作為父id的有五個兒子BCDEF,將繼續插入資料庫,此時他們以新生成的NewAid作為pid這個欄位值插入資料庫,生成了新的Bid,Cid,Did,Eid,Fid.依此類推BCDEF下面有B1,B2;C1,C2,C3;D1,D2,D3,D4>>>>>>>繼續插入
 
        分析一下,其實就是說在前端時,每個節點都有個treeid和pid,傳到後臺是為了找他們的子節點,
當每個節點生成了新的id後,就將這個id作為子節點的pid繼續插入子節點,又生成一批新的id作為子節點的子節點的pid

        所以,難點就是遞迴怎麼寫,因為我以前遇到的少,所以卡殼了一天才完成,fastJson上篇已經說明

直接上程式碼說明,無關程式碼可忽略

public void insertCatalog(){
    HttpServletRequest req = ServletActionContext.getRequest();
    String datalist = req.getParameter("datalist");
    JSONArray ja  = JSON.parseArray(datalist);//解析傳過來的json串 
    JSONObject rootJO = ja.getJSONObject(0);//入口ja的第一個物件約定是根節點,先插入
    String newid = add2DB(rootJO);//
插入後返回id String oldid = rootJO.getString("id");//這是前臺傳過來的id ja.remove(0);//插入一條就移走一條,減少後面遞迴的量 insert(ja, newid,oldid);//遞迴插入的入口 }

 

//遞迴插入
public void insert(JSONArray ja,String newid,String oldid) {
    if (newid==null || "".equals(newid) ) return;         //如果返回的newid不對勁,終止 
if (ja.isEmpty()) return; //遞迴插入的出口 for (int i=0; i<ja.size(); i++) { JSONObject jo = ja.getJSONObject(i); if ( oldid.equals( jo.get("pId") ) ) { //根據每個節點的id尋找他的兒子們 jo.remove("parentID"); //如果找到了,換掉parentID這個屬性的值 jo.put("parentID", newid); //給他賦的值就是每次生成的newid String newCreateId = add2DB(jo); //插入,返回新的id String currentId = jo.getString("id"); //這是當前被插入的節點的id,作為下次尋找兒子們用 ja.remove(i); //這裡每插入一條,移除 insert(ja, newCreateId,currentId); //遞迴方法   }   } }

 

 
add2DB這個方法就不記錄了,就是茶如資料庫,返回插入後的新id的方法 


到此,這個遞迴就完成了,測試插入成功,寫完了還是覺得意猶未盡~~~~~~~~~~~~~~~~~~~~~~~~~
**********************************************************************************************************************************************
以上遞迴方法測試,會跳過偶數下標的資料,現在沒有找到問題出在哪兒,最大的可能是,for迴圈內部又使用了遞迴方法
查閱過相關技術大牛的文章,有人對for迴圈內部再遞迴的情形做過研究,我自己覺得這樣設計(有時候不得不)對程式邏輯流需要一定的
想象力。再者需要了解一個東西。遞迴只會往深處呼叫,不存在什麼跳出for迴圈外呼叫自己。

首先,java的堆存物件,棧存基本資料型別和物件引用地址這個是基本的,也就是說,每一次遞迴方法呼叫都會重新產生很多變數值,如果再加上for迴圈,那麼棧裡面的變數增加會很快,棧中的變數一般只會在程式結束的時候才會被清空,所以當迴圈很多,或者遞迴很深的時候,jvm的棧可能會被塞爆。這個僅做一個瞭解。

我要說的是,遞迴的時候,不用擔心遞迴之前的變數被覆蓋或清空了,因為每次呼叫方法,棧都會重新給這個方法(要產生變數)分配屬於該方法的記憶體,也就是分配一塊兒地方,所以遞迴只會往更深的方向執行方法,打個比方,A方法在內部遞迴呼叫自己,好比A克隆了A1,遞迴的時候調的A1,然後繼續克隆繼續呼叫,體現在棧裡面就是不斷分配空間給這些A1,A2,A3、、、、、、

這個遞迴的方法我改了一下,

public void insert(JSONArray ja,String newid,String oldid) {
  if (newid==null||"".equals(newid)) return;
  //遞迴插入的出口
  if(ja.isEmpty())return;
  List<JSONObject> joList = search(ja,oldid);//先搜尋兒子們,存到joList 中
  ja.removeAll(joList);//搜尋完了全部移除
  for(int i=0;i<joList.size();i++){
    JSONObject jo = joList.get(i);
    jo.remove("parentID");
    jo.put("parentID", newid);
    String newCreateId = add2DB(jo);
    String currentId = jo.getString("id");
    insert(ja,newCreateId,currentId);
  }
}

 

新加的搜尋的方法
public List<JSONObject> search(JSONArray ja,String id){
  if(ja.isEmpty())return null;
  List<JSONObject> joList = new ArrayList<JSONObject>();
  for(int i=0;i<ja.size();i++){
    JSONObject jo = ja.getJSONObject(i);
    if(id.equals(jo.get("pId"))){
    joList.add(jo);
    }
  }
  return joList;
} 

 

整體思路是:首先最開始的時候插入根節點產生id,根節點移除
然後由根節點原來的id在資料包ja裡,調search方法返回他的所有子節點裝進list,根節點的子節點們移除
遍歷list迴圈插入,每插入一條產生新id,再調用搜索方法搜尋他的兒子們,移除,插入,依次往深處遞迴執行
直到“第一條”的id下的所有兒子們全部插入完成,開始插入“第二條”
第二條的模式和第一條一樣,也是插入,產生id,找兒子,插入兒子,兒子找孫子繼續插入
直到第一批的節點全部插入完成,所有節點就全部插入完成