写点什么

ArrayList(Java8,阿里大牛把「服务雪崩」玩到了极致

用户头像
Geek_f90455
关注
发布于: 1 小时前

问题:ArrayList 类常量 EMPTY_ELEMENTDATA 和 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 怎么理解?是不是多余?


这两个类常量都是空 Object 数组的引用,都代表 ArrayList 实例的空状态,也即是 elementData 数组中还没有元素。但是 EMPTY_ELEMENTDATA 是使用带初始化值的构造方法(有参构造函数,一个是指定初始容量,一个是指定初始集合)时使用的,DEFAULTCAPACITY_EMPTY_ELEMENTDATA 是使用默认的构造方法,也即是无参的构造方法时使用的。


问题:ArrayList 是怎样实现扩充的?


扩容是发生在添加操作前的,要保证要添加元素在 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();

### 总目录展示
该笔记共八个节点(由浅入深),分为三大模块。
**高性能**。 秒杀涉及大量的并发读和并发写,因此支持高并发访问这点非常关键。该笔记将从设计数据的动静分离方案、热点的发现与隔离、请求的削峰与分层过滤、服务端的极致优化这4个方面重点介绍。
**一致性**。 秒杀中商品减库存的实现方式同样关键。可想而知,有限数量的商品在同一时刻被很多倍的请求同时来减库存,减库存又分为“拍下减库存”“付款减库存”以及预扣等几种,在大并发更新的过程中都要保证数据的准确性,其难度可想而知。因此,将用一个节点来专门讲解如何设计秒杀减库存方案。
**高可用**。 虽然介绍了很多极致的优化思路,但现实中总难免出现一些我们考虑不到的情况,所以要保证系统的高可用和正确性,还要设计一个PlanB来兜底,以便在最坏情况发生时仍然能够从容应对。笔记的最后,将带你思考可以从哪些环节来设计兜底方案。
-----
篇幅有限,无法一个模块一个模块详细的展示(这些要点都收集在了这份《高并发秒杀顶级教程》里),觉得有需要的码友们,麻烦各位转发一下(可以帮助更多的人看到哟!)[点这里,即可获得免费下载的方式!!](https://gitee.com/vip204888/java-p7)
![](https://static001.geekbang.org/infoq/41/41770a453b8fbb395bfeb27a190d8383.png)
![](https://static001.geekbang.org/infoq/b8/b82a41a73d3d6b1645369dc770a55304.png)
由于内容太多,这里只截取部分的内容。需要这份《高并发秒杀顶级教程》的小伙伴,麻烦各位帮忙点赞分享支持一下(可以帮助更多的人看到哟!)
复制代码


用户头像

Geek_f90455

关注

还未添加个人签名 2021.07.06 加入

还未添加个人简介

评论

发布
暂无评论
ArrayList(Java8,阿里大牛把「服务雪崩」玩到了极致