ArrayList动态扩容原理&深拷贝与浅拷贝

36 篇文章 0 订阅
        ArrayList由数组实现,当增加或删除数据元素时,除了最后位置的元素外, ArrayList需要移动其被添加或删除的元素后面的全部元素。对于遍历所有的元素,ArrayList是具有良好的效率的。
特点:
  • 查、改效率高:直接通过下标获取元素,直接通过下标设置元素。因为数组元素在物理上是连续的,知道其中一个的内存地址,就可以推算出其他元素的内存地址。
  • 尾部增、删效率高:每次增加或删除,都需要移动指定位置后面的元素,所以增删的位置越靠前,需要移动的元素就越多,效率就越低;反之,效率就越高。
  • 线程不安全:未实现同步。
适用场景:
ArrayList 适用于存储需要显示并不会对其进行修改的数据,仅仅是用于显示。
ArryList中的数组扩容:
ArrayList 中数组的动态扩容的实现,采用的是 Arrays.copyOf( T[] original,  int newLength ) 方法。它的默认大小 10,最大长度 Integer.MAX_VALUE - 8。最后通过下面的方式,实现了数组的动态扩容:
elementData = Arrays.copyOf(elementData, newCapacity);

数组扩容的原理

       因为数组的大小是固定不变的,所以数组对象是不可以扩容的。但是使用数组复制的方法,是可以实现数组扩容的,Arrays.copyOf() 可以方便的创建数组副本。创建副本的同时增加数组的长度,便实现了数组的动态扩容。
System.arraycopy(Object src, int srcPos, Object dest, int destPos, int srcLength),

上面的代码,也是实现复制数组的一个方法,Arrays.copyOf() 方法中就是创建一个新的数组,然后调用 System.arraycopy() 来实现的。

System.arraycopy
看源码可知,它调用的是native层的方法,实现的数组复制。它是线程不安全的。
@FastNative
public static native void arraycopy(
                    Object src,
                    int srcPos,
                    Object dest,
                    int destPos,
                    int length
)

参数介绍:

  • Src          源数组;
  • srcPos    源数组中开始复制的位置;
  • dest        目标数组,即新的数组;
  • destPos  目标数组中,粘贴数据的起始位置;
  • length     源数组中需要复制的长度;

深拷贝 和 浅拷贝

拷贝前和拷贝之后,如果被拷贝的对象在堆里的地址没有变化,则称为 “浅拷贝”;
拷贝前和拷贝之后,如果被拷贝的对象在堆里的地址发生变化,则称为 “深拷贝”;
1. 浅拷贝
首先我们来看一下下面的代码,一个 Teacher 类,一个 Student类,Teacher 类持有 Student 类的引用。然后我们通过 Cloneable,对 Teacher 类 进行拷贝操作,来详细讲解一下 深拷贝与浅拷贝的区别:
public class Teacher implements Cloneable{
	
	private String name;

	private int age;

	private Student stu;

	@Override
	public Teacher clone() {
		try {
			return (Teacher) super.clone();
		} catch (CloneNotSupportedException e) {
			throw new AssertionError();
		}
	}
   	......
}
public class Student {
	
	private long stuNo;
	
	private String stuName;

	public long getStuNo() {
		return stuNo;
	}

	public void setStuNo(long stuNo) {
		this.stuNo = stuNo;
	}
    ......
}

主程序代码如下:

public class ShallowMain {
	public static void main(String[] args) {
		Student student = new Student(2016021006, "李四");
		Teacher teacher = new Teacher("鲁班", 24);
		teacher.setStu(student);

		// 克隆教师对象
		Teacher teacher1 = teacher.clone();

		System.out.println(teacher == teacher1);
		// 原对象
		System.out.println(teacher);
		// 克隆对象
		System.out.println(teacher1);

		System.out.println("-------------------修改克隆对象的name以及年龄---------------------");
		// 修改克隆对象的name以及年龄
		teacher1.setName("鲁班大师");
		teacher1.setAge(34);

		// 原对象
		System.out.println("原对象:" + teacher);
		// 克隆对象
		System.out.println("克隆对象:" + teacher1);

		System.out.println("-------------------修改原对象的学生属性---------------------");
		// 修改原对象的学生属性
		student.setStuName("张三");
		student.setStuNo(666666);

		// 原对象
		System.out.println("原对象:" + teacher);
		// 克隆对象
		System.out.println("克隆对象:" + teacher1);
	}
}

输出结果如下:

得出结论 :

  • teacher == teacher1打印出的结果是false,则说明是创建了一个新的对象,说明 clone() 方法导致的一个结果是 深拷贝。

  • 修改克隆出的对象的name和age属性,发现并不会影响原对象的属性值,说明值类型的成员也发生了改变

  • 修改克隆出的对象的学生属性,导致原对象的学生属性也被修改了,说明 Student 对象被拷贝的仅仅只是 地址空间里的引用地址,并未创建新的 Student 对象出来,所以对于 Student 而言,是一个 浅拷贝 的操作。

浅拷贝中:
值类型的字段会复制一份,而 引用类型的字段拷贝的仅仅是引用地址,而该引用地址指向的实际对象空间其实只有一份。

 2. 深拷贝

如果 Student 类也执行 Cloneable 的操作,则将是一个 深拷贝的过程

public class Student implements Cloneable{
	
	private long stuNo;
	
	private String stuName;

	@Override
	public Student clone() {
		try {
			return (Student) super.clone();
		} catch (CloneNotSupportedException e) {
			throw new AssertionError();
		}
	}
	......
}
public class Teacher implements Cloneable{
	
	private String name;
	
	private int age;
	
	private Student stu;

	@Override
	public Teacher clone() {
		try {
            //先克隆Teacher对象
			Teacher teacher = (Teacher) super.clone();
            //克隆Student对象,复值给克隆出的teacher对象中的stu属性
			teacher.stu = stu.clone();
			return teacher;
		} catch (CloneNotSupportedException e) {
			throw new AssertionError();
		}
	}
	
	......
}

执行结果如下:

深拷贝中:
深拷贝相对于浅拷贝,在复制 值类型的字段时,也会将引用类型所指向的内存拷贝一份。

Cloneable 属于深拷贝,重新分配内存,并将元素复制过去。对克隆后的数组进行修改,不会影响到原数组。

System.arraycopy 则是直接复制内存引用,不改变内存地址。因此,在数据量很大的时候,效率就很明显了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值