首先我们知道ArrayList就是数组列表,主要用来装载数据,当我们装载的是基本类型的数据int,long,boolean,short,byte…的时候我们只能存储他们对应的包装类,它的主要底层实现是数组Object[] elementData。
与它类似的是LinkedList,和LinkedList相比,它的查找和访问元素的速度较快,但新增,删除的速度较慢。这是数组和链表的主要区别
ArrayList和LinkedList的线程都不安全但不影响我们正常情况下使用.因为在正常场景下我们基本都是查询,很少涉及频繁的增删.如果涉及到增删的情况很多,我们可以使用LinkedList. 如果需要用线程安全则应该使用Vector.
没有一个集合工具能完美实现这三点,因为数据结构的特性是优劣共存.
ArrayList的添加数据
在我们印象中数组的大小是固定的,但ArrayList也是数组,而他不断添加进数据仍然是可以的.
ArrayList可以通过构造方法在初始化的时候指定底层数组的大小。
通过无参构造方法的方式ArrayList()初始化,则赋值底层数Object[] elementData为一个默认空数组Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}所以数组容量为0,只有真正对数据进行添加add时,才分配默认DEFAULT_CAPACITY = 10的初始容量。
他的两个构造函数,无参会默认大小,有参则判断参数
而在添加数据超过他的容量时,ArrayList会进行扩容,扩容后的长度是原长度+原长度/2.然后把原数据原封不动的复制过来,并继续添加数据.
他有指定index新增,也有直接新增的,在这之前他会有一步校验长度的判断ensureCapacityInternal,就是说如果长度不够,是需要扩容的。
在扩容的时候,老版本的jdk和8以后的版本是有区别的,8之后的效率更高了,采用了位运算,右移一位,其实就是除以2这个操作。

指定位置新增的时候,在校验之后的操作很简单,就是数组的copy,大家可以看下代码。
比如有下面这样一个数组我需要在index 5的位置去新增一个元素A
那从代码里面我们可以看到,他复制了一个数组,是从index 5的位置开始的,然后把它放在了index 5+1的位置
给我们要新增的元素腾出了位置,然后在index的位置放入元素A就完成了新增的操作了
而ArrayList删除index部分和增加类似,都是找出index部位,然后都要进行复制操作,也正是因此,在数据量极大的情况下会导致增删效率低下
所以ArrayList的增删快慢取决于距离末端的长度,用来做堆栈比较合适,push和pop操作完全不涉及数据移动.而做队列的话不太合适
相比较来说,数组则很适合做队列,简单点说就是使用两个偏移量来标记数组的读位置和写位置,如果超过长度就折回到数组开头,前提是它们是定长数组.
ArrayList不会初始化数组大小
而且将构造函数与initialCapacity结合使用,然后使用set()会抛出异常,尽管该数组已创建,但是大小设置不正确。
使用sureCapacity()也不起作用,因为它基于elementData数组而不是大小。
还有其他副作用,这是因为带有sureCapacity()的静态DEFAULT_CAPACITY。
进行此工作的唯一方法是在使用构造函数后,根据需要使用add()多次。
遍历方面ArrayList比LinkedList快很多
论遍历ArrayList要比LinkedList快得多,ArrayList遍历最大的优势在于内存的连续性,CPU的内部缓存结构会缓存连续的内存片段,可以大幅降低读取内存的性能开销
最后ArrayList的线程是否安全
当然是不安全,线程安全版本的数组容器是Vector。
Vector的实现很简单,就是把所有的方法统统加上synchronized就完事了。
也可以不使用Vector,用Collections.synchronizedList把一个普通ArrayList包装成一个线程安全版本的数组容器也可以,原理同Vector是一样的,就是给所有的方法套上一层synchronized。