浅克隆

浅克隆,将目标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
    22
    public 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
    16
    public 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);
    }
    输出结果
    1
    2
    3
    4
    [
    Book [id=1, name=《Java零基础从入门到崩溃》],
    Book [id=2, name=《10年Java大牛之秃头经验》]
    ]
    参考 clone() 的源码,如下图所示
    返回的是新的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 {
@SuppressWarnings("unchecked")
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
26
public 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大牛之秃头经验》]
]