一,问题背景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方法也能够实现浅拷贝。
当写代码时,对象属性有引用类型的时候,操做该对象须要注意浅拷贝深拷贝的区别!