文章目录
一、ArrayList是什么?
ArrayList是java集合框架的一部分,用于存储多个对象的工具类.
1.特点:
- 对象按照插入的顺序存储.
- 同一个对象可以被重复插入,包括Null值.
- 支持根据下标随机快速访问,查询效率高,时间复杂度为:O(1).
- 容量不够时,会自动扩容.
- 删除跟插入元素时会涉及到数据的前移跟后移,效率不高,时间复杂度为:O(n).
- 只能存储引用数据类型.
- 线程不安全.
2.核心属性:
- Object[] elementData:用于实际存储数据的数组,默认初始容量10,当容量不够时,每次扩容0.5倍,即:新容量= 旧容量 + ( 旧容量>>1 );最小存储容量: 0(创建对象时,指定容量为0)最大存储容量: Integer.MAX_VALUE 即:2的31次方 - 1。
- int size:记录当前数组中存储数据的个数。
- int modCount:记录此列表在结构上被修改的次数,在Iterator(迭代器)中修改列表结构(新增,删除)时,会抛出:ConcurrentModificationException 异常;如果要在循环中删除某个元素,只能通过for循环来删除。
二、使用场景?
当要存储的对象的个数是不确定且查询操作较多时,可以使用ArrayList。
三、底层是如何实现的?
1.添加、删除
ArrayList类内部持有一个Object的数组以及一个用于记录Object数组大小的size(初始值为0);
当调用add(未指定index)方法时,将对象存入size所指向的数组下标位置,然后size++;
当调用add(指定index) 方法时,先将指定的index以及index后面的值,通过System.arraycopy()方法将数据往后移动一位,然后用新对象覆盖掉index位置的旧对象,最后size++;
当调用remove(指定index)方法时,先通过System.arraycopy()方法将index后面所有的对象往前移动一位(将index位置的值覆盖掉),然后–size,最后将size所指向的数组下标值设置为Null.
2. 扩容
当调用ArrayList类的add方法插入对象时,会先检查Object数组的容量是否足够,不够时则计算出一个新的容量(新容量=旧容量 + (旧容量>>1) 即新容量是旧容量的1.5倍),然后再根据新的容量创建出一个新的Object数组,最后通过System.arraycopy方法将旧数组中的数据全部复制到新数组中去.
四、与其相似的类还有哪些?它们与ArrayList类的区别是什么?
1. 数组
- 数组的容量是固定的,一旦创建其容量不可再更改,当要存储的数据的个数是不确定的情况下,不适合使用.
- 数组没有提供查询当前已存储数据的个数的方法.
2. Vector
与ArrayList一样,也继承了AbstractList,内部的实现逻辑也基本一致,不过它所提供的方法都用synchronized关键字修饰过,所以它是线程安全的,所以它的性能也相对要差许多. 且目前已不推荐使用.
五、推荐用法
先预估要存储的数据量,然后在创建ArrayList对象的时候指定初始容量,尽量避免ArrayList扩容,因为扩容时需要将旧数组中的数据复制到新数组中去,这个操作会造成一定的性能损耗.