Java集合学习(一)ArrayList源码学习
前言
侵删,学习记录笔记。并不会写出所有函数的分析,但我会文后放入推荐资料
在Java中,List是一种常用的数据类型,是一种有序的数据结构。
JDK版本
1.8
ArrayList
最常用的List实现类,内部通过数组实现。由于通过数组实现,所以获取元素速度极快,它允许对元素进行快速随机访问。对ArrayList的操作并不是线程安全的,建议单线程使用ArrayList,多线程使用Vector或者CopyOnWriteArrayList
ArrayList是一个可以动态增长容量的动态数组。虽然访问速度很快,但数组的缺点是每个元素之间不能有间隔,当数组大小不满足需要扩展容量时,就要将原数组的数据移动到新的存储空间,当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制和移动操作,代价较高。ArrayList适合随机查找和遍历,不适合插入和删除。
List<Integer>list=new ArrayList<Integer>();
list.add(1);
System.out.println(list.get(0));
构造方法
ArrayList() 构造一个初始容量为10的空列表
注意,无参构造函数最开始会将elementData这个内部数组引用一个内部static final Object[]空数组,只有第一次进行add操作时才会调用扩容使得elementData扩容成10长度的数组
ArrayList(Collection<? extends E> c)构造一个包含指定元素的列表
ArrayList(int initialCapacity) 构造一个指定初始容量的空列表,
指定的容量必须大于0
add函数
图 2 ArrayList add函数图
- add(E e);在列表末尾添加元素
- add(int index,E e)在指定位置添加元素
分析
//确保容量是否足够
ensureCapacityInternal(size + 1);
//添加元素,size+1 ,返回true
elementData[size++] = e;
return true;
前面add函数中ensureCapacityInternal(size + 1),传入了size+1并赋值给minCapacity给后续函数用值
calculateCapacity()计算容量,如果当前数组是默认的空数组则从size+1、默认容量(10)中选取最大值并返回,否则返回minCapacity。
ensureExplicitCapacity()函数获取了calculateCapacity()返回的容量,并检查如果calculateCapacity()返回的容量大于当前数组长度,便进行扩容grow()操作。
首先将数组长度增加到原来数组的一半oldCapacity+oldCapacity/2
如果长度还是不够传入的minCapacity长,则数组扩容到minCapacity长度。
小结
当对数据进行add操作时,会先判断当前数组容量是否足够容纳新元素,不够则会在原有的数组长度下再增加一半长度,如果扩容函数传入的minCapacity仍然大于已经扩容的长度,那就将数组扩容至minCapacity长度。
remove(int )函数
首先判断index是否越界,然后将要删除的元素后面的元素整体往前移动1下标,最后将最后一个位置的数组元素设null,并size-1
get(int )函数
判断index是否越界,根据数组下标返回相应元素
clear()函数
修改次数+1,遍历数组并逐个设null,size设0
ArrayList的序列化以及反序列化
提问:为什么ArrayList 中 elementData 使用 transient 修饰?
答:ArrayList是一个动态数组,由于扩容机制,数组内并非所有空间都被使用,为了防止被自动序列化,便使用transient修饰elementData。同时ArrayList自己实现了序列化和反序列化。
transient :当属性使用transient关键字修饰时,序列化对象时,这个属性不会被序列化
分析
在writeObject(java.io.ObjectInputStream s)中,首先将ArrayList.size序列化,然后根据size来序列化数组元素。
在readObject(java.io.ObjectInputStream s)中,根据读取到的size来计算容量 注意calculateCapacity()函数,前文提到过此函数的容量返回机制
来确定是否进行扩容,最后存入反序列化得来的元素。
iterator()
ArrayList自己实现了自己的Iterator
后续函数就不截图了,重点关注在函数内部的throw new ConcurrentModificationException();
当修改次数和期望的修改次数不同时,会报多线程异常。
参考资料
《Java核心面试知识整理.pdf》