ArrayList(Java8(2),spring教程

扩容是发生在添加操作前的,要保证要添加元素在 elementData 数组中有位置,也即是 size 加上要添加的元素个数要小于 capacity(size + num <= capacity 就说明容量是充足的),所以在添加方法中,先调用 ensureCapacityInternal(int) 方法来确保 elementData 容量充足,然后再进行具体的添加操作。如果 ensureCapacityInternal 方法(ensureCapacityInternal 方法中有调用了其他方法)发现数组容量不够了,就会扩容。扩容实际的方法是 grow(int) 方法,使用位运算符来使数组的容量扩容 1.5 倍。但是需要注意的是,没有指定初始化值的 ArrayList 空实例,第一次扩容并不是以 1.5 倍扩容的,而是使用的默认容量 10,所以网上很多直接说 ArrayList 扩容是 1.5 倍也有不当之处,这点从 JDK 源码中可以很明确的看出来。

如果在构造 ArrayList 实例时,指定初始化值(初始化容量或者集合),那么就会创建指定大小的 Object 数组,并把该数组对象的引用赋值给 elementData;如果不指定初始化值,在第一次添加元素值时会使用默认的容量大小 10 作为 elementData 数组的初始容量,使用 Arrays.conpyOf() 方法创建一个 Object[10] 数组。

问题:Arrays.copyOf 方法和 System.arraycopy 方法的区别?

Arrays.copyOf(T[], int length) 方法是 Arrays 工具类中用来进行任意类型数组赋值(包括 null 值),并使数组具有指定长度的方法,ArrayList 中用这个方法来实现 elementData 数组的元素移动。但实际上 Arrays.copyOf 方法最终调用的是 System.arraycopy(U[], int srcPos, T[], desPos, int length) 方法,这个方法是一个本地方法,不能直接看源码。U 和 T 都是一种泛型,只是为了便于区分,U 表示的是原始数组(源数组)类型,T 表示的是存放拷贝值的数组(目标数组)类型,srcPos 是指原始数组中的起始位置(从原始数组的哪个位置开始拷贝),desPos 是指存放拷贝值的数组拷贝起始位置(从目标数组的哪个位置插入这些拷贝的值),length 表示要拷贝的元素数量(要从原始数组中拷贝多少个)。

问题:ArrayList 的 add 操作优化?

核心就是避免 ArrayList 内部进行扩容。

1、对于普通少量的 add 操作,如果插入元素的个数已知,最好使用带初始化参数的构造方法,避免 ArrayList 内部再进行扩容,提高性能。

2、对于大量的 add 操作,最好先使用 ensureCapacity 方法来确保 elementData 数组中有充足的容量来存放我们后面 add 操作的元素,避免 ArrayList 实例内部进行扩容。上面提到的 ensureCapacityInternal 方法是一个私有方法,不能直接调用,而 ensureCapacity 方法是一个共有方法,专门提供给开发者使用的,提高大量 add 操作的性能。

测试代码如下:


ArrayList<Integer> list1 = new ArrayList<>();



int addCount = 1_000_000; // 这个值不要太小,否则效果不明显



// 没有优化



long begin1 = System.currentTimeMillis();



for (int i = 0; i < addCount; i++) {



    list1.add(i);



}



long end1 = System.currentTimeMillis();



long cost1 = end1 - begin1;



ArrayList<Integer> list2 = new ArrayList<>();



// 有优化



list2.ensureCapacity(addCount);



long begin2 = System.currentTimeMillis();



for (int i = 0; i < addCount; i++) {



    list2.add(i);



}



long end2 = System.currentTimeMillis();



long cost2 = end2 - begin2;



System.err.println("大量 add 操作没有优化前的时间花费:" + cost1); // 32 毫秒



System.err.println("大量 add 操作优化后的时间花费:" + cost2); // 14 毫秒

总结

虽然我个人也经常自嘲,十年之后要去成为外卖专员,但实际上依靠自身的努力,是能够减少三十五岁之后的焦虑的,毕竟好的架构师并不多。

架构师,是我们大部分技术人的职业目标,一名好的架构师来源于机遇(公司)、个人努力(吃得苦、肯钻研)、天分(真的热爱)的三者协作的结果,实践+机遇+努力才能助你成为优秀的架构师。

如果你也想成为一名好的架构师,那或许这份Java成长笔记你需要阅读阅读,希望能够对你的职业发展有所帮助。

CodeChina开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频】

image

开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频】](https://codechina.csdn.net/m0_60958482/java-p7)**

[外链图片转存中…(img-O00Dq4SG-1630632325534)]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值