前言
这篇文章咱介绍下 ArrayList面试考点 有备无患 😄
默认大小
默认大小为10
扩容为原来的一半 扩容一次以后为15
扩容使用Arrays.copyOf(elementData, size, Object[].class);
懒加载
1.7之后都是延迟初始化
无参构造器,指向的是默认容量大小的Object数组,注意使用无参构造函数的时候并没有直接创建容量为10的Object数组,而是采取懒加载的策略:使用
DEFAULTCAPACITY_EMPTY_ELEMENTDATA(实际容量为0)作为标识,在真正add元素时才会开辟Object数组
当我们向数组中添加第一个元素时,DEFAULTCAPACITY_EMPTY_ELEMENTDATA将会知道数组该扩充多少
内部结构
ArrayList内部结构,是一个Object[]类型的数组
构造函数
- ArrayList(int initialCapacity)
构造方法:表示接受指定容量值,初始化创建数组,
- ArrayList()
构造方法:是默认的构造方法,它创建一个空数组
- ArrayList(Collection<? extends E> c)
构造方法:接收一个Collection的实体,将该Collection实体转换为ArrayList对象。
failFast机制
- ArrayList只能在单线程环境下使用,在多线程环境下会出现并发安全问题
- modCount主要用于记录对ArrayList的修改次数,如果一个线程操作期间modCount发生了变化即,有多个线程同时修改当前这个ArrayList,此时会抛出
- “ConcurrentModificationException”异常,这又被称为“failFast机制”
- 在很多非线程安全的类中都有failFast机制:HashMap、 LinkedList等。
- 这个机制主要用于迭代器、加强for循环等相关功能,也就是一个线程在迭代一个容器 时,如果其他线程改变了容器内的元素,迭代的这个线程会抛出“ConcurrentModificationException”异常
在使用Java的集合类的时候,如果发生ConcurrentModificationException,优先考虑fail-fast有关的情况,实际上这可能并没有真的发生并发,只是Iterator使用了fail-fast的保护机制,只要他发现有某一次修改是未经过自己进行的,那么就会抛出异常
双向列表
- LinkedList底层采用双向列表实现,所以在添加新元素的时候要先构造一个Node对象(item为我们加入的值),只需要将Node的next赋值为新的Node即可
- 相对于ArrayList而言,LinkedList可以更加高效的插入元素,因为它不会涉及到ArrayList扩容。但也因为这种数据结构,导致它不具备有随机访问的能力
如何保证线程安全
1、线程安全的方法
List list = Collections.synchronizedList(new ArrayList<>());
Collections提供了方法synchronizedList保证list是同步线程安全的
2、CopyOnWriteArrayList:写时复制
- CopyOnWrite容器是写时复制的容器,往一个容器添加元素的时候,不直接往当前的容器Object[]添加,而是先将当前容器Object[] 进行Copy,复制出一个新的容器Object[] newElements,然后往新的容器Object[] newElement里面添加元素,添加完元素之后,再将原容器的引用指向新的容器,setArray(newElements );
- 这样做的好处是可以对CopyOnWriter容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素,所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。
是否允许空值
允许空值和重复元素
时间复杂度
底层基于数组实现,所以其可以保证在 O(1) 复杂度下完成随机查找操作
static修饰的EMPTY_ELEMENTDATA和DEFAULTCAPACITY_EMPTY_ELEMENTDATA