一、List 的浅克隆
![](https://img.haomeiwen.com/i7038163/b0914837d65cd229.png)
List 是 Java 容器中最常用的顺序存储数据结构之一。有些时候将一组数据取出放到一个 List 对象中,但是可能会很多处程序要读取或者是修改。尤其是并发处理的话,显然有的时候一组数据是不够用的。这个时候通常会克隆出一个甚至多个 List 来执行更多的操作。
List<String> souString = new ArrayList<>();
souString.add("xxx1");
souString.add("xxx2");
souString.add("xxx3");
1️⃣使用List实现类的构造方法
List<String> tarString = new ArrayList<>(souString);
2️⃣遍历循环复制
List<String> tarString = new ArrayList<>();
for(int i = 0, l = souString.size(); i < l; i++){
tarString.add(listString0.get(i));
}
3️⃣调用 Collections 的静态工具方法 Collections.copy
List<String> tarString = new ArrayList<>(
Arrays.asList(new String[souString.size()]));
Collections.copy(tarString,souString);
4️⃣使用 System.arraycopy 方法进行复制
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 的某一个元素的值,如果其他列表中的值没有受到影响那么就是复制成功了。
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));
}
输出结果符合预期,这几个方法都实现了 list 的复制。修改本体并没有影响到复制体。但是对象呢?
二、List 的深克隆
![](https://img.haomeiwen.com/i7038163/7a45cd711ec6c25f.png)
创建一个 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 类只存储了一个字符串,同样可以模拟之前的几种复制方法,代码如下:
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());
}
}
}
由于本体表的某个数据的修改,导致后续的克隆表的数据全被修改了,这四种方法无一例外。也就是说对于自定义 POJO 类而言上述的四种方法都未能实现对元素对象自身的复制。复制的只是对象的引用或是说地址。
解析:
List 自身是一个对象,在存储类类型的时候,只负责存储地址。而存储基本类型的时候,存储的就是实实在在的值。其实上边的案例也说明了这点,因为修改 PojoStr-List 的时候直接的修改了元素本身而不是使用的 ArrayList 的 set(index,object) 方法。所以纵然有千千万万个 List,元素还是那么几个。无论是重新构造,Collections 的复制方法,System 的复制方法,还是手动去遍历,结果都一样,这些方法都只改变了 ArrayList 对象的本身,简单的添加了几个指向老元素的地址,而没做深层次的复制。(压根没有 new 新对象的操作出现。)当然有的时候,确实需要将这些元素也都复制下来而不是只是用原来的老元素。然而很难在 List 层实现这个问题。毕竟依照 Java 的语言风格,也很少去直接操作这些埋在堆内存中的数据,所有的操作都去针对能找到它们的地址了。地址没了自身还会被 GC 干掉。所以只好一点点的去遍历去用 new 创建新的对象并赋予原来的值。
如何深克隆?方法如下:
注:前提是 T 如果是 Pojo 类的话,必须实现序列化接口,这是对象进入 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 + '\'' + '}';
}
}
网友评论