java arraylist复制_从ArrayList说起的JAVA复制与参数传递机制

这两者都算是java基础中的基础,平常写代码可能并没有过多的去深究它,但这样容易引发一些不可预知的BUG。

这里有一个简单的类,文章中会提到多次。

一个学生类,它有两个属性,String类型的name与Integer类型的age。

public class Student {

private String name;

private Integer age;

public Student() {

}

public Student(String name, Integer age) {

this.name = name;

this.age = age;

}

@Override

public String toString() {

return "Student{" +

"name='" + name + '\'' +

", age=" + age +

'}';

}

}

首先我们有一个List。

ArrayList originalList = new ArrayList();

list1.add(new Student("五更琉璃", 15));

list1.add(new Student("高坂桐乃", 14));

1、先说说最简单粗暴的复制。

ArrayList copyList = new ArrayList<>();

copyList = originalList;

copyList.set(1,new Student("土间埋",16));

copyList直接获得originalList的引用

originalList ==>

Student{name='五更琉璃', age=15}

Student{name='土间埋', age=16}

copyList ==>

Student{name='五更琉璃', age=15}

Student{name='土间埋', age=16}

结果如下,我们发现,即使只改变了copyList的元素element,原来的ArrayList也跟着变了。

originalList = {ArrayList@531} size = 2

0 = {Student@533} "Student{name='五更琉璃', age=15}"

1 = {Student@534} "Student{name='土间埋', age=16}"

copyList = {ArrayList@531} size = 2

0 = {Student@533} "Student{name='五更琉璃', age=15}"

1 = {Student@534} "Student{name='土间埋', age=16}"

打个断点我们可以发现,无论是ArrayList,还是里面的引用,它们的内存地址是完全一样的。

也就是说,直接赋值 copyList = originalList;的这种方法,很难称得上是一种复制。

2、使用clone方法进行复制

ArrayList copyList = (ArrayList) originalList.clone();

copyList.set(1,new Student("土间埋",16));

结果如下:看起来正常了,我们的originalList并没有因为copyList的set方法而改变。

originalList ==>

Student{name='五更琉璃', age=15}

Student{name='高坂桐乃', age=14}

copyList ==>

Student{name='五更琉璃', age=15}

Student{name='土间埋', age=16}

然而打开断点会发现,不是那么一回事。

我们发现,originalList和copyList的指向确实不同了。 然而List中的元素,指向的还是同一块地址。

originalList = {ArrayList@531} size = 2

0 = {Student@534} "Student{name='五更琉璃', age=15}"

1 = {Student@535} "Student{name='高坂桐乃', age=14}"

copyList = {ArrayList@532} size = 2

0 = {Student@534} "Student{name='五更琉璃', age=15}"

1 = {Student@539} "Student{name='土间埋', age=16}"

也就是说,如果我们不进行set重新修改元素的指向,而是直接改变元素内的属性。

ArrayList copyList = (ArrayList) originalList.clone();

copyList.get(1).setName("土间埋");

copyList.get(1).setAge(16);

从断点中我们可以得到我们的预期。originalList 中的第一个元素,也会被改变。

originalList = {ArrayList@463} size = 2

0 = {Student@466} "Student{name='五更琉璃', age=15}"

1 = {Student@467} "Student{name='土间埋', age=16}"

copyList = {ArrayList@464} size = 2

0 = {Student@466} "Student{name='五更琉璃', age=15}"

1 = {Student@467} "Student{name='土间埋', age=16}"

从这里可以看出,ArrayList提供的clone方法,实际上是一种浅复制。

也就是它不是一种递归复制。

它只是改变了顶层,copyList 的引用。

我们看看源码

public Object clone() {

try {

ArrayList> v = (ArrayList>) 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(e);

}

}

源码中,先是clone了一个新的ArrayList:v

然后将原ArrayList中的数据直接复制到了新的ArrayList中。

再看看ArrayList中的add与addAll方法,实际上也是如出一辙。

public boolean add(E e) {

ensureCapacityInternal(size + 1); // Increments modCount!!

elementData[size++] = e;

return true;

}

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;

}

它们只是把元素的引用复制了一遍。也就是说调用clone、add、addAll方法,当改变元素中的属性时,新List中的元素也会跟着改变。

java的参数传递也是如此,java中,参数的传递都是值传递。当一个对象实例作为一个参数被传递到方法中时,参数的值就是该对象的引用一个副本。

当这个副本的属性在方法内部被改变时,这个副本的正本也会被改变。

那么就针对当前业务来说,如何实现深克隆?

ArrayList copyList = new ArrayList<>();

for (Student student : originalList) {

copyList.add(student.clone());

}

copyList.get(1).setName("土间埋");

copyList.get(1).setAge(16);

粗暴实现:我们发现它们的地址已经完全不同了。

originalList = {ArrayList@464} size = 2

0 = {Student@467} "Student{name='五更琉璃', age=15}"

1 = {Student@468} "Student{name='高坂桐乃', age=14}"

copyList = {ArrayList@465} size = 2

0 = {Student@472} "Student{name='五更琉璃', age=15}"

1 = {Student@473} "Student{name='土间埋', age=16}"

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值