浅谈Java中ArraryList的浅克隆和深克隆
浅克隆
浅克隆,将目标ArraryList
每个元素对象的引用指向原始ArraryList
对应元素对象在栈空间的内存地址。
只是复制了原始ArrayList中元素对象的引用,目标ArrayList和原始ArrayList是一荣俱荣一损俱损的,并非实现真正意义上的“新的ArrayList”。
简单来说,不管是改变原始ArrayList的中的元素对象,还是改变目标ArrayList中的元素对象,只要是浅克隆,所有的 目标ArraryList 和 原始ArraryList 也都会一起跟着发生改变
最常见的几种应用浅克隆的方式
list.addAll()
将原始ArrayList整体添加到目标ArrayList中
- 使用 addAll(Collection<? extends E> c) 的语法,是将原始ArrayList整体添加的目标ArrayList的尾部。
例如:desList.addAll(srcList)
- 使用 addAll(int index, Collection<? extends E> c) 的语法,其中index用于指定 collection 的第一个元素所插入位置的索引。简单来说,就是将原始ArrayList整体添加的目标ArrayList的指定index下标位置。
例如:desList.addAll(2, srcList)
输出结果1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22public static void main(String[] args) {
//原始List
List<SmsCoupon> srcList = new ArrayList<>();
Book item1 = new Book();
item1.setId("1");
item1.setName("《Java零基础从入门到崩溃》");
srcList.add(item1);
Book item2 = new Book();
item2.setId("2");
item2.setName("《10年Java大牛之秃头经验》");
srcList.add(item2);
//目标List
List<SmsCoupon> desList = new ArrayList<>();
//1.将原始List添加到目标List尾部
desList.addAll(srcList);
System.out.println(desList);
//2.将原始List添加到目标List指定索引位置(索引下标为2的位置)
desList.addAll(2, srcList);
}参考1
2
3
4[
Book [id=1, name=《Java零基础从入门到崩溃》],
Book [id=2, name=《10年Java大牛之秃头经验》]
]addAll()
的源码,如下图所示
不难看出,addAll()
只是作为一个整体实例,把原始List循环进目标List中。
所以说,即便自己创建一个新的目标ArrayList
,不断的将原始ArrayList
循环添加进目标ArrayList
,也只是将引用填进去,并未改变原始引用类型。1
2
3
4
5
6
7
8
9
10
11/**
* addAll() 源码
*/
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}list.clone()
也是同样将将原始ArrayList整体添加到目标ArrayList中输出结果1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public static void main(String[] args) {
//原始List
ArrayList<Book> srcList = new ArrayList<>();
Book item1 = new Book();
item1.setId("1");
item1.setName("《Java零基础从入门到崩溃》");
srcList.add(item1);
Book item2 = new Book();
item2.setId("2");
item2.setName("《10年Java大牛之秃头经验》");
srcList.add(item2);
//目标List
ArrayList<Book> desList = (ArrayList<Book>)srcList.clone();
System.out.println(desList);
}参考 clone() 的源码,如下图所示1
2
3
4[
Book [id=1, name=《Java零基础从入门到崩溃》],
Book [id=2, name=《10年Java大牛之秃头经验》]
]
返回的是新的ArrayList元素对象,但是存储数据的elementData,存储的对象还是指向了原始ArrayList存储的元素对象。
简单来说,ArrayList这个类实现了深克隆,但是实际上存储元素对象的方式还是浅克隆
当然,如果非得想clone()实现深克隆,那么就得通过重写clone的方式实现,
我个人比较喜欢自己写个方法作为工具方法直接调用实现1
2
3
4
5
6
7
8
9
10
11
12
13
14
15/**
* clone() 源码
*/
public Object clone() {
try {
ArrayList<E> v = (ArrayList<E>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError();
}
}
深克隆
深克隆,将原始ArraryList
中的元素对象,在堆内存
中重新开辟空间,把这些元素对象作为新的元素对象进行存储。
深克隆出来的 目标ArraryList
与 原始ArraryList
在操作时互不影响
下面列举一个深克隆的方法,可以作为工具进行引用1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18/**
* Arrary的深度Copy
* @param srcList 原始list
* @return 目标list
* @throws IOException
* @throws ClassNotFoundException
*/
public List<Book> ArraryListDepthCopy(List<Book> srcList) throws IOException, ClassNotFoundException {
//序列化
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(srcList);
//反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
List<Book> desList = (List<Book>) ois.readObject();
return desList;
}
测试结果1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26public static void main(String[] args) {
//原始List
List<Book> srcList = new ArrayList<>();
Book item1 = new Book();
item1.setId("1");
item1.setName("《Java零基础从入门到崩溃》");
srcList.add(item1);
Book item2 = new Book();
item2.setId("2");
item2.setName("《10年Java大牛之秃头经验》");
srcList.add(item2);
//目标List
List<Book> desList = new ArrayList<>();
try {
desList = ArraryListDepthCopy(srcList);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 输出结果
System.out.println(desList);
}
输出结果1
2
3
4
5// 输出结果
[
Book [id=1, name=《Java零基础从入门到崩溃》],
Book [id=2, name=《10年Java大牛之秃头经验》]
]