(1)继承自抽象类AbstractList;
(2)实现了List接口,拥有List的基本功能;
(3)实现了RandomAccess接口,拥有随机读写的功能;
(4)实现了Cloneable,可以被克隆;
(5)实现了Serializable接口,并重写了序列化和反序列化方法,使得ArrayList拥有更好的序列化性能。
注:
关于第3种构造方法,首先调用给定集合(collection)的toArray()方法将其转换为一个数组并赋值给 elementData ;size 是集合的大小,当通过别的集合来构造ArrayList的时候,需要赋值 size。size不为0时,接下来的if条件语句是判断c.toArray()返回的结果是否正确,如果不正确则利用Arrays.copyOf方法将集合c中的元素复制到elementData数组中;size为0则将EMPTY_ELEMENTDATA空实例赋给elementData。
注:在上述add操作中,按照方法调用的顺序看下来,
(1)在 calculateCapacity(int minCapacity) 方法中,判断了是否使用的默认无参构造,如果是则返回的最小容量为Math.max(DEFAULT_CAPACITY,size+1),反之返回传入的size+1;
(2)modCount字段是用来记录修改过容量的次数,调用ensureExplicitCapacity()方法意味着确定修改容器的大小,即确认扩容;
(3)在grow(int minCapacity)方法中,先按原数组长度elementData.length的1.5倍扩容,如果1.5倍扩容不能满足最小容量,则将最小容量作为扩容后的数组容量;如果按1.5倍扩容超过MAX_ARRAY_SIZE,则对newCapacity进行合理性约束;
(4)最后,拷贝原数组中的数据到扩容后的新数组,并赋给elementData。
注:在上述的add操作中,
(1)首先检查指定的插入位置是否合法;
(2)接着进行数组扩容操作;
(3)然后是影响ArrayList插入删除效率的关键操作——数据搬移,将指定位置开始及其以后的所有元素向后移动,为所插入元素腾出空间;
(4)最后插入元素,元素个数size加1。
注:和上述两个添加元素的操作不同之处在于是批量插入元素,需要先将集合转成数组,若传入的集合为null将抛出空指针异常,其他操作类似。
注:
第1个remove()是删除指定位置处的元素,需要进行下标合法性检查,按照索引直接取得元素值,返回删除的数据,将后面的元素向前移;
第2个remove()是删除指定元素,需要对元素是否为null分情况讨论,元素为null时不能调用元素对象的equals方法。(经验证,当列表中有多个相同数据时,只会删除遍历遇到的第1个数据,正如源码中所示,删除一次就return)
注:
上述方法为批量删除元素和批量保留元素(实质也是批量删除,即删除不在集合中的所有元素),都会先对集合 c 进行是否为null判断处理,调用Objects.requireNonNull方法,若为null抛出空指针异常。如果在操作中途出现异常,会导致 r != size,则将出现异常后面的数据全部复制覆盖到数组中,如下源码所示:
注:
对指定索引进行合法性检查,oldValue保留旧值,然后用新值覆盖旧值,返回oldValue。
注:
在获取集合的迭代器的时候,去new了一个Itr对象,而Itr实现了Iterator接口,我们主要关注Iterator接口的常用的方法hasNext(),next(),remove()方法。
(1)cursor:下一个元素的下标
(2)lastRet:上一次返回元素的下标
(3)hasNext():判断是否还有元素
(4)next():按照cursor的递增逐个取出元素
(5)remove():删除元素,如果异步进行了删除操作将会抛出异常
1.有序:指的是插入元素的顺序和取出元素的顺序一致;
2.可重复:指的是可以插入重复的元素;
3.按索引查找元素效率高,增删元素效率较低(尾插尾删例外):
原因是查找的时候根据索引可一步到位获取元素值,而增删操作涉及大量的数据搬移操作;
4.线程不安全:
首先源码中的各个操作都没有synchronized字段,以add为例,当插入元素的时候,elementData[size++] = e可以理解为两步操作,
①elementData[size] = e;②size++;我们都知道CUP是切换进程运行的,在单线程中这样是没有问题的,但是很多情况是在多线程中使用ArrayList的,这时候比如有两个线程,线程 A 先将元素存放在位置 0。但是此时 CPU 调度线程A暂停,线程 B 得到运行的机会。线程B也向此 ArrayList 添加元素,因为此时 size 仍然等于 0 ,所以线程B也将元素存放在位置0,然后线程B增加size的值,接着线程A运行,也增加 size 的值,这样最后得到的元素实际上只有一个,存放在位置 0,而 size 却等于 2,因此造成了线程不安全。
5.适用场景:高频的查询操作和尾部增删操作的情况下可以选用。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。