ArrayList史上最详细源码分析

今天电话面试,面试官问到ArrayList中add方法的原理,我一下蒙了,这个用的最多的一种数据结构被我忽视了,没答上来,有时候用的最多的反而最自信,最自信的东西最容易忽视其中的细节,因此今天决定扒扒源码。

1 ArrayList是什么?

一种用来存放数据的数据结构。

它的底层是一个数组,用来存放数据。

2 ArrayList的特点?

  • 底层为数组。
  • 所存储的数据在内存中连续。
  • 查询的时候以地址偏移量来计算数据位置,因此时间复杂度为O(1)。//这里是指用索引作为参数查询,而非使用数组相应位置的内容作为参数查询。
  • 添加数据有两种方式,添加在数组的最后,或者根据输入的索引位置添加到数组的相应位置。
  • 第一种方式添加数据时,会判断数组有没有初始化,没有初始化时,会把数组初始化为10大小的容量,并且保证数组容量足够,不足的情况会调用扩容函数。并且会调用修改次数+1的函数。
  • 第二种方式添加数据时,先判断index参数是否数组下表越界。再执行与第一种方式一样的操作(上一行的蓝色文字),之后再进行插入,插入时,索引位置和之后的元素向后移一位。
  • ArrayList有两种初始化的方式,一种空参默认的初始化,一种指定容量的初始化方式。
  • 数组采用默认初始化方式时,默认的容量为10。
  • 扩容时,以1.5倍原数组最大容量的方式扩容。

3.源码分析(1460行)

源码比较多的,我另开博客做了解读,源码较短较简单的,我直接说明了源码的作用。

ArrayList源码分析
源码类型 源码概览 源码分析
属性 private static final long serialVersionUID = 8683452581122892189L; 序列化时用来验证数据一致性的值
属性 private static final int DEFAULT_CAPACITY = 10; 空参构造初始化数组时,数组的默认大小为10
属性 private static final Object[] EMPTY_ELEMENTDATA = {}; 一个空的数组
属性 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; 共享空数组实例,用于默认大小的空实例。我们将其与DEFAULTCAPACITY_EMPTY_ELEMENTDATA分开来,以了解添加第一个元素时要扩容多少。
属性 transient Object[] elementData; 一个临时使用的数组
属性 private int size; 当前数组里所存放元素的个数
方法 public ArrayList(int initialCapacity) {} 有参的构造方法,指定数组的初始容量大小
方法 public ArrayList() {} 默认的空参构造。默认的初始化容量大小为10
方法 public ArrayList(Collection c) {} 按照入参集合的迭代器返回的元素的顺序构造包含指定集合的元素的数组。
方法 public void trimToSize() {} 把数组的容量变为当前数组中包含元素的大小,此方法用来最小化数组在存储空间所占用的大小。
方法 public void ensureCapacity(int minCapacity) {} 用来在添加数据时,保证数组容量大于数组所包含的元素个数的。
方法 private void ensureCapacityInternal(int minCapacity) {} 当数组为空数组时,最小容量设置为传入的参数,并且最后调用函数(下一行)
方法 private void ensureExplicitCapacity(int minCapacity) {}

增加一次修改数组的次数,修改数组后,容量若小于数组元素个数,开始扩容。

属性 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; 能分配的数组最大的大小。某些虚拟机在数组中保留一些头字。尝试分配较大的数组可能导致内存不足错误:请求的数组大小超过了VM限制,因此数组的最大容量 MAX_ARRAY_SIZE要在Integer.MAX_VALUE上要减去8个!!Integer.MAX_VALUE2^{31}-1
方法 private void grow(int minCapacity) {} https://blog.csdn.net/qq_29519041/article/details/86246881
方法 private static int hugeCapacity(int minCapacity) {} https://blog.csdn.net/qq_29519041/article/details/86246881
方法 public int size() {} 返回数组中存放的元素个数
方法 public boolean isEmpty() {} 当数组中没有元素时 返回true 否者为false
方法 public boolean contains(Object o) {} 作用:判断数组中是否包含 o 对象,原理:调用了indexOf做判断。看下一行
方法 public int indexOf(Object o) {}

https://blog.csdn.net/qq_29519041/article/details/86259595   

就是普通的遍历查找,并返回下标。

方法 public int lastIndexOf(Object o) {} 与indexOf方法的唯一区别是,遍历从尾部开始。其他和indexOf完全一样。
方法 public Object clone() {}

浅拷贝!,就是对一个对象只复制他们对应的引用,这些复制后的引用指向的还是之前的对象。就是说,对象的实例只有一个。但是有两个引用指向他们。我自己的理解。若有错误请留言

关于深拷贝浅拷贝相关优秀博文:

https://blog.csdn.net/github_38687585/article/details/79926909

https://blog.csdn.net/DeMonliuhui/article/details/54572908

方法 public Object[] toArray() {}

返回包含此列表中所有元素的数组。

(从第一个元素到最后一个元素)。

返回的数组将是“安全的”,因为此列表不维护对它的引用。(换句话说,此方法必须分配一个新数组)。

因此,调用方可以自由修改返回的数组。

此方法充当基于数组和基于集合的API之间的桥梁。

方法 public T[] toArray(T[] a) {}

https://rogerfederer.iteye.com/blog/788795

 

它与上面的方法的区别是,它会以传入的数组的类型来返回对应类型的数组。

方法 E elementData(int index) {} 返回数组索引位置的元素,它将会被get(int index)调用
方法 public E get(int index) {} 返回数组索引位置的元素,首先会调用rangeCheck判断是否数组索引越界,之后调用elementData(int index)方法。
方法 public E set(int index, E element) {} 把element替换掉index位置的元素,并返回被替换掉的元素
方法 public boolean add(E e) {} 在尾部添加元素,之前会对元素修改次数做累加。
方法 public void add(int index, E element) {} 在数组相应位置添加元素,对应位置及以后的元素向后移一位,这也是数组插入数据慢的原因。需要移动后面的元素。
方法 public E remove(int index) {} https://blog.csdn.net/qq_29519041/article/details/86316290
方法 public boolean remove(Object o) {} https://blog.csdn.net/qq_29519041/article/details/86316290
方法 private void fastRemove(int index) {}  
方法 public void clear() {}  
方法 public boolean addAll(Collection c) {}  
方法 public boolean addAll(int index, Collection c) {}  
方法 protected void removeRange(int fromIndex, int toIndex) {}  
方法 private void rangeCheck(int index) {}  
方法 private void rangeCheckForAdd(int index) {}  
方法 private String outOfBoundsMsg(int index) {}  
方法 public boolean removeAll(Collection c) {}  
方法 public boolean retainAll(Collection c) {}  
方法 private boolean batchRemove(Collection c, boolean complement) {}  
方法 private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException{
 
方法 private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {}
 
方法 public ListIterator listIterator(int index) {}  
方法 public ListIterator listIterator() {}  
方法 public Iterator iterator() {}  
方法 private class Itr implements Iterator {}  
方法 public boolean hasNext() {}  
方法 public E next() {}  
方法 public void remove() {}  
方法 public void forEachRemaining(Consumer consumer) {}  
方法 final void checkForComodification() {}  
内部类ListItr private class ListItr extends Itr implements ListIterator {}  
ListItr的方法 public boolean hasPrevious() {  
ListItr的方法 public int nextIndex() {  
ListItr的方法 public int previousIndex() {  
ListItr的方法 public E previous() {  
ListItr的方法 public void set(E e) {  
ListItr的方法 public void add(E e) {  
方法 public List subList(int fromIndex, int toIndex) {  
静态方法  static void subListRangeCheck(int fromIndex, int toIndex, int size) {  
     

1460行的代码太多了,慢慢更吧...

-------------    最后结束修改于2019年1月 10日 22:45   待续...    -------------

-------------    最后结束修改于2019年1月 11日 20:46   待续...    -------------

 

 

 

 

 

你可能感兴趣的:(ArrayList,集合,Jdk1.8源码解读,Java)