一,問題背景java
最近遇到一塊代碼,看了半天沒有看明白如何實現樹形結構的。debugger以後發現原理,實際上是利用了java對象是引用類型,利用淺拷貝來實現樹型結構。ios
/** * * @param table "樹型結構中的全部節點" * @param childrenField "固定key,名稱爲children" * @param idField "每一個節點id" * @param parentIdField "子節點與父節點的關係屬性parentId" * @return */ public static ArrayList list2Tree(List table, String childrenField, String idField, String parentIdField) { ArrayList tree = new ArrayList(); Map hash = new HashMap();//裝載全部對象 id,object格式 for (int i = 0, l = table.size(); i < l; i++) { Map t = (Map)table.get(i); hash.put(t.get(idField), t); } for (int i = 0, l = table.size(); i < l; i++) { Map t = (Map)table.get(i); Object parentID = t.get(parentIdField); if (parentID == null || parentID.toString().equals("-1"))//父元素 { tree.add(t);//向樹型結構裏放入父節點 continue; } Map parent = (Map)hash.get(parentID);//子元素
//這邊修改引用類型的map,修改其屬性值會改變,tree裏面的父節點會相應發生變化
if (parent == null) { tree.add(t); continue; } List children = (List)parent.get(childrenField); if (children == null) { children = new ArrayList(); parent.put(childrenField, children); } children.add(t); } return tree; }
針對發現的問題,這邊本身查閱資料複習一下淺拷貝深拷貝。數組
二,淺拷貝網絡
淺拷貝是按位拷貝對象,它會建立一個新對象,這個對象有着原始對象屬性值的一份精確拷貝。若是屬性是基本類型,拷貝的就是基本類型的值;若是屬性是內存地址(引用類型),拷貝的就是內存地址 ,所以若是其中一個對象改變了這個地址,就會影響到另外一個對象。即默認拷貝構造函數只是對對象進行淺拷貝複製(逐個成員依次拷貝),即只複製對象空間而不復制資源。ide
示例代碼:函數
public class test1Main { public static void main (String[] args){ Son son = new Son(66); Person p1 = new Person("tony",son); Person p2 = new Person(p1); p1.setSonName("bella"); p1.getSon().setAge(88);//s是對象是引用,改變s全部引用s的值都會改變 System.out.println("p1==="+p1); // p1===Person [sonName=bella, son=Son [age=88]] System.out.println("p2==="+p2); // p2===Person [sonName=tony, son=Son [age=88]] } }
三,深拷貝工具
深拷貝會拷貝全部的屬性,並拷貝屬性指向的動態分配的內存(會另外開闢一個內存空間)。當對象和它所引用的對象一塊兒拷貝時即發生深拷貝。深拷貝相比於淺拷貝速度較慢而且花銷較大。當咱們須要new兩個徹底不同的對象時,須要序列化來實現深拷貝。this
示例代碼:spa
public class Test2Main{ public static void main(String[] args) { Son son = new Son(66); Person p1 = new Person("小湯", son); //系列化實現深拷貝 ByteArrayOutputStream bos = new ByteArrayOutputStream(); try { ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(p1); oos.flush(); oos.close(); bos.close();//這裏是假關閉 //ByteArrayOutputStream/ByteArrayInputStream流是以一個字節數組來操做。 //建立輸入流的時候,要先提供一個輸入源byte[],以後讀取實際上就是讀取這個byte[]的內容; //而輸出流則是將數據寫入一個內置的數組,通常磁盤和網絡流寫完以後就拿不到寫完的內容, //但因爲這個是寫到了一個數組中,因此仍是能夠獲取數據的 ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())); Person p2 = (Person) ois.readObject(); p1.setSonName("小湯原始"); p1.getSon().setAge(44); System.out.println("p1===="+p1); //p1====Person [sonName=小湯原始, son=Son [age=44]] System.out.println("p2===="+p2); //p2====Person [sonName=小湯, son=Son [age=66]],徹底一個新的對象 p2.getSon().setAge(33); System.out.println("p2-1===="+p2); // 修改一個新的對象的值 p2-1====Person [sonName=小湯, son=Son [age=33]] System.out.println("p1==="+p1); //修改新的對象的值並無改變 p1, p1===Person [sonName=小湯原始, son=Son [age=44]] System.out.println("p1.hashCode()===="+ p1.hashCode()); //p1.hashCode()====1118140819 System.out.println("p2.hashCode()===="+ p2.hashCode()); //p2.hashCode()====1956725890 //hashCode 不一樣 證實兩個對象的內存地址徹底不同 } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
可複用的深拷貝工具類:debug
public class SerializedClone { @SuppressWarnings("unchecked") public static <T extends Serializable> T clone(T obj) { T cloneObj = null; try { //寫入字節流 ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutputStream obs = new ObjectOutputStream(out); obs.writeObject(obj); obs.close(); //分配內存,寫入原始對象,生成新對象 ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray()); ObjectInputStream ois = new ObjectInputStream(ios); //返回生成的新對象 cloneObj = (T) ois.readObject(); ois.close(); } catch (Exception e) { e.printStackTrace(); } return cloneObj; }
淺拷貝深拷貝示例代碼person和son類的定義:
public class Person implements Serializable { private String sonName; private Son son; public Person(String sonName,Son son) { this.sonName = sonName; this.son = son; } public Person(Person person) { this.sonName = person.sonName; this.son = person.son; } public String getSonName() { return sonName; } public void setSonName(String sonName) { this.sonName = sonName; } public Son getSon() { return son; } public void setSon(Son son) { this.son = son; } @Override public String toString() { return "Person [sonName=" + sonName + ", son=" + son + "]"; } }
public class Son implements Serializable{ private int age; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Son(int age) { this.age = age; } @Override public String toString() { return "Son [age=" + age + "]"; } }
若是person,son實現Cloneable接口,使用clone方法也能夠實現淺拷貝。
當寫代碼時,對象屬性有引用類型的時候,操做該對象須要注意淺拷貝深拷貝的區別!