一黔龟、List 的淺克隆
List 是 Java 容器中最常用的順序存儲數(shù)據(jù)結(jié)構(gòu)之一斜友。有些時候?qū)⒁唤M數(shù)據(jù)取出放到一個 List 對象中,但是可能會很多處程序要讀取或者是修改伤为。尤其是并發(fā)處理的話蜓萄,顯然有的時候一組數(shù)據(jù)是不夠用的偿短。這個時候通常會克隆出一個甚至多個 List 來執(zhí)行更多的操作。
List<String> souString = new ArrayList<>();
souString.add("xxx1");
souString.add("xxx2");
souString.add("xxx3");
1??使用List實現(xiàn)類的構(gòu)造方法
List<String> tarString = new ArrayList<>(souString);
2??遍歷循環(huán)復(fù)制
List<String> tarString = new ArrayList<>();
for(int i = 0, l = souString.size(); i < l; i++){
tarString.add(listString0.get(i));
}
3??調(diào)用 Collections 的靜態(tài)工具方法 Collections.copy
List<String> tarString = new ArrayList<>(
Arrays.asList(new String[souString.size()]));
Collections.copy(tarString,souString);
4??使用 System.arraycopy 方法進(jìn)行復(fù)制
String[] strs = new String[souString.size()];
System.arraycopy(listString0.toArray(), 0, strs, 0, souString.size());
List<String> tarString = Arrays.asList(strs);
5??使用Stream的方式copy
List destList = srcList.stream().collect(Collectors.toList());
6??使用list.addAll()
List destList = new ArrayList();
destList.addAll(srcList);
【測試】試著改變 souString 的某一個元素的值凯旋,如果其他列表中的值沒有受到影響那么就是復(fù)制成功了呀潭。
souString.set(0, "rock");
for(int i = 0, l = souString.size(); i < l; i++){
System.out.println("souString第"+i+"個值:"+souString.get(i));
System.out.println("tarString第"+i+"個值:"+tarString.get(i));
}
輸出結(jié)果符合預(yù)期,這幾個方法都實現(xiàn)了 list 的復(fù)制至非。修改本體并沒有影響到復(fù)制體蜗侈。但是對象呢?
二睡蟋、List 的深克隆
創(chuàng)建一個 Pojo 類:
import java.io.Serializable;
public class PojoStr implements Serializable{
private String str = "";
public String getStr(){
return str;
}
public void setStr(String str){
this.str = str;
}
}
這個 Pojo 類只存儲了一個字符串踏幻,同樣可以模擬之前的幾種復(fù)制方法,代碼如下:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<PojoStr> souString = new ArrayList<>();
PojoStr p1 = new PojoStr();
p1.setStr("xxx1");
souString.add(p1);
PojoStr p2 = new PojoStr();
p2.setStr("xxx2");
souString.add(p2);
PojoStr p3 = new PojoStr();
p3.setStr("xxx3");
souString.add(p3);
List<PojoStr> tarString1 = new ArrayList<>(souString);
List<PojoStr> tarString2 = new ArrayList<>();
for(int i = 0, l = souString.size(); i < l; i++)
tarString2.add(souString.get(i));
List<PojoStr> tarString3 = new ArrayList<>(Arrays.asList(new PojoStr[souString.size()]));
Collections.copy(tarString3,souString);
PojoStr[] strs = new PojoStr[souString.size()];
System.arraycopy(souString.toArray(), 0, strs, 0, souString.size());
List<PojoStr> tarString4 = Arrays.asList(strs);
souString.get(0).setStr("rock");
for(int i = 0, l = souString.size(); i < l; i++) {
System.out.println("souString第"+i+"個值:"+souString.get(i).getStr());
System.out.println("tarString1第"+i+"個值:"+tarString1.get(i).getStr());
}
}
}
由于本體表的某個數(shù)據(jù)的修改戳杀,導(dǎo)致后續(xù)的克隆表的數(shù)據(jù)全被修改了该面,這四種方法無一例外夭苗。也就是說對于自定義 POJO 類而言上述的四種方法都未能實現(xiàn)對元素對象自身的復(fù)制。復(fù)制的只是對象的引用或是說地址隔缀。
解析:
List 自身是一個對象题造,在存儲類類型的時候,只負(fù)責(zé)存儲地址猾瘸。而存儲基本類型的時候界赔,存儲的就是實實在在的值。其實上邊的案例也說明了這點牵触,因為修改 PojoStr-List 的時候直接的修改了元素本身而不是使用的 ArrayList 的 set(index,object) 方法淮悼。所以縱然有千千萬萬個 List,元素還是那么幾個揽思。無論是重新構(gòu)造袜腥,Collections 的復(fù)制方法,System 的復(fù)制方法钉汗,還是手動去遍歷羹令,結(jié)果都一樣,這些方法都只改變了 ArrayList 對象的本身损痰,簡單的添加了幾個指向老元素的地址福侈,而沒做深層次的復(fù)制。(壓根沒有 new 新對象的操作出現(xiàn)卢未。)當(dāng)然有的時候癌刽,確實需要將這些元素也都復(fù)制下來而不是只是用原來的老元素。然而很難在 List 層實現(xiàn)這個問題尝丐。畢竟依照 Java 的語言風(fēng)格显拜,也很少去直接操作這些埋在堆內(nèi)存中的數(shù)據(jù),所有的操作都去針對能找到它們的地址了爹袁。地址沒了自身還會被 GC 干掉远荠。所以只好一點點的去遍歷去用 new 創(chuàng)建新的對象并賦予原來的值。
如何深克率ⅰ譬淳?方法如下:
注:前提是 T 如果是 Pojo 類的話,必須實現(xiàn)序列化接口盹兢,這是對象進(jìn)入 IO 流的基本要求
1??【使用序列化方法】
@SuppressWarnings("unchecked")
public static <T> List<T> deepCopy(List<T> src) {
try (ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
ObjectOutputStream outputStream = new ObjectOutputStream(byteOut);
) {
outputStream.writeObject(src);
try (ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
ObjectInputStream inputStream = new ObjectInputStream(byteIn);
) {
return (List<T>) inputStream.readObject();
}
} catch (Exception e) {
ThrowableUtils.getString(e);
}
return Collections.emptyList();
}
2??【clone方法】
public class A implements Cloneable {
public String name[];
public A(){ name=new String[2]; }
public Object clone() {
A o = null;
try {
o = (A) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
} return o;
}
}
for(int i=0;i<n;i+=){
copy.add((A)src.get(i).clone());
}
三邻梆、Map的深克隆
public class CopyMap {
public static void main(String[] args) {
Map<String, Integer> map11 = new HashMap<String, Integer>();
map11.put("key1", 1);
Map<String, Integer> map22 = map11;
map11.put("key1", 3);
System.out.println(map11);
System.out.println(map22);
System.out.println("------------------------------");
Map<String, Integer> map1 = new HashMap<String, Integer>();
map1.put("key1", 1);
Map<String, Integer> map2 = new HashMap<String, Integer>();
map2.putAll(map1);
map1.put("key1", 3);
System.out.println(map1);
System.out.println(map2);
System.out.println("------------------------------");
Map<String, Dog> map3 = new HashMap<String, Dog>();
Dog dog1 = new Dog("Dog1");
map3.put("key1", dog1);
Map<String, Dog> map4 = new HashMap<String, Dog>();
map4.putAll(map3);
System.out.println(map4);
map3.get("key1").setName("dog3");
System.out.println(map4);
System.out.println("------------------------------");
Map<String, Dog> map5 = new HashMap<String, Dog>();
Dog dog5 = new Dog("Dog5");
map5.put("key5", dog5);
Map<String, Dog> map6 = (Map<String, Dog>) deepClone(map5);
System.out.println(map6);
map5.get("key5").setName("Dog7");
System.out.println(map6);
}
public static Object deepClone(Object obj) {
try {// 將對象寫到流里
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bo);
oo.writeObject(obj);// 從流里讀出來
ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi = new ObjectInputStream(bi);
return (oi.readObject());
} catch (Exception e) {
return null;
}
}
}
class Dog implements Serializable {
private String name;
public Dog(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override public String toString() {
return "Dog{" + "name='" + name + '\'' + '}';
}
}