ArrayList源码分析
一、ArrayList简介
ArrayList 的底层是数组队列,相当于动态数组。与 Java 中的数组相比,它的容量能动态增长。在添加大量元素前,应用程序可以使用ensureCapacity操作来增加 ArrayList 实例的容量。这可以减少递增式再分配的数量。
它继承于 AbstractList,实现了 List, RandomAccess, Cloneable, java.io.Serializable 这些接口。
在我们学数据结构的时候就知道了线性表的顺序存储,插入删除元素的时间复杂度为O(n),求表长以及增加元素,取第 i 元素的时间复杂度为O(1)
ArrayList 继承了AbstractList,实现了List。它是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。
ArrayList 实现了RandomAccess 接口, RandomAccess 是一个标志接口,表明实现这个这个接口的 List 集合是支持快速随机访问的。在 ArrayList 中,我们即可以通过元素的序号快速获取元素对象,这就是快速随机访问。
ArrayList 实现了Cloneable 接口,即覆盖了函数 clone(),能被克隆。
ArrayList 实现java.io.Serializable 接口,这意味着ArrayList支持序列化,能通过序列化去传输。
和 Vector 不同,ArrayList 中的操作不是线程安全的!所以,建议在单线程中才使用 ArrayList,而在多线程中可以选择 Vector 或者 CopyOnWriteArrayList。
二、源码分析
一、属性:
- 默认初始容量大小
private static final int DEFAULT_CAPACITY = 10;
- 空数组(用于空实例)。
private static final Object[] EMPTY_ELEMENTDATA = {};
- 用于默认大小空实例的共享空数组实例。我们把它从
EMPTY_ELEMENTDATA
数组中区分出来,以知道在添加第一个元素时容量需要增加多少。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
elementData
数组用来存储ArrayList中的元素,从这个可以看出,ArrayList是底层是借组于数组来实现的。
transient Object[] elementData;
size
用来记录ArrayList中存储的元素的个数。
private int size;
二、构造方法:
- 带初始容量参数的构造函数。(用户自己指定容量)
public ArrayList(int initialCapacity) { if (initialCapacity > 0) { //创建initialCapacity大小的数组 this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { //创建空数组 this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } }
- 默认构造函数,DEFAULTCAPACITY_EMPTY_ELEMENTDATA 为0.初始化为10,也就是说初始其实是空数组 当添加第一个元素的时候数组容量才变成10
public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
- 构造一个包含指定集合的元素的列表,按照它们由集合的迭代器返回的顺序。
public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { // c.toArray 可能返回的不是Object类型的数组所以加上下面的语句用于判断, //这里用到了反射里面的getClass()方法 if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; } }
上面将容器Collection转化为数组赋给数组elementData,还对Collection转化是否转化为了Object[]进行了检查判断。如果Collection为空,则就将空的常量数组对象EMPTY_ELEMENTDATA赋给了elementData;
三、add方法:
add(E e)
:将元素加入到List的末尾。
public boolean add(E e) { modCount++; add(e, elementData, size); return true; }
上面代码调用了另一个
add方法
private void add(E e, Object[] elementData, int s) { //s为size,当size等于数组长度时,数组扩容 if (s == elementData.length) elementData = grow(); elementData[s] = e; size = s + 1; }
add(int index, E element)
:将element
插入index
位置
public void add(int index, E element) { //对index进行界限检查 rangeCheckForAdd(index); modCount++; final int s; Object[] elementData; //当size等于数组长度时,数组扩容 if ((s = size) == (elementData = this.elementData).length) elementData = grow(); //arraycopy()这个实现数组之间复制的方法一定要看一下,下面就用到了arraycopy()方法实现数组自己复制自己 System.arraycopy(elementData, index, elementData, index + 1, s - index); elementData[index] = element; size = s + 1; }
addAll(Collection<? extends E> c)
:按指定集合的Iterator返回的顺序将指定集合中的所有元素追加到此列表的末尾。
public boolean addAll(Collection<? extends E> c) { Object[] a = c.toArray(); modCount++; int numNew = a.length; if (numNew == 0) return false; Object[] elementData; final int s; if (numNew > (elementData = this.elementData).length - (s = size)) elementData = grow(s + numNew); System.arraycopy(a, 0, elementData, s, numNew); size = s + numNew; return true; }
四、扩容核心方法:
ArrayList的扩容机制提高了性能,如果每次只扩充一个,那么频繁的插入会导致频繁的拷贝,降低性能,而ArrayList的扩容机制避免了这种情况。
- 要分配的最大数组大小
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
- 最少扩容为
size+1
private Object[] grow() { return grow(size + 1); }
- 把数组复制到扩容后的数组
private Object[] grow(int minCapacity) { return elementData = Arrays.copyOf(elementData, newCapacity(minCapacity)); }
- 扩容核心
private int newCapacity(int minCapacity) { // oldCapacity为旧容量,newCapacity为新容量 int oldCapacity = elementData.length; //将oldCapacity 右移一位,其效果相当于oldCapacity /2, //我们知道位运算的速度远远快于整除运算,整句运算式的结果就是将新容量更新为旧容量的1.5倍, int newCapacity = oldCapacity + (oldCapacity >> 1); //然后检查新容量是否大于最小需要容量,若还是小于最小需要容量,那么就把最小需要容量当作数组的新容量, if (newCapacity - minCapacity <= 0) { //数组是DEFAULTCAPACITY_EMPTY_ELEMENTDATA时,那第一次增加元素时扩容为10 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) return Math.max(DEFAULT_CAPACITY, minCapacity); if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return minCapacity; } //再检查新容量是否超出了ArrayList所定义的最大容量, //若超出了,则调用hugeCapacity()来比较minCapacity和 MAX_ARRAY_SIZE, //如果minCapacity大于MAX_ARRAY_SIZE,则新容量则为Interger.MAX_VALUE,否则,新容量大小则为 MAX_ARRAY_SIZE。 return (newCapacity - MAX_ARRAY_SIZE <= 0) ? newCapacity : hugeCapacity(minCapacity); } //比较minCapacity和 MAX_ARRAY_SIZE private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
四、remove方法
1.
remove(int index):删除index的元素
public E remove(int index) { Objects.checkIndex(index, size); final Object[] es = elementData; @SuppressWarnings("unchecked") E oldValue = (E) es[index]; fastRemove(es, index); return oldValue; }
上面的代码用到了
fastRemove,将值
private void fastRemove(Object[] es, int i) { modCount++; final int newSize; if ((newSize = size - 1) > i) System.arraycopy(es, i + 1, es, i, newSize - i); es[size = newSize] = null;// clear to let GC do its work }
remove(Object o)
:移除指定元素
public boolean remove(Object o) { final Object[] es = elementData; final int size = this.size; int i = 0; found: { //如果为null,遍历与null比较 if (o == null) { for (; i < size; i++) if (es[i] == null) break found; } else { //如果不为空,遍历与o比较 for (; i < size; i++) if (o.equals(es[i])) break found; } return false; } fastRemove(es, i); return true; }
removeAll(Collection<?> c)
:从此列表中删除指定集合中包含的所有元素。
public boolean removeAll(Collection<?> c) { return batchRemove(c, false, 0, size); }
五、get方法
get(int index):获得index出的值
public E get(int index) { //边界检查 Objects.checkIndex(index, size); return elementData(index); }
六、set方法
set(int index, E element):将
element复制给
index处
public E set(int index, E element) { Objects.checkIndex(index, size); E oldValue = elementData(index); elementData[index] = element; return oldValue; }
七、clear方法
clear():清除ArrayList中所有的元素。直接将数组中的所有元素设置为null即可,这样便于垃圾回收。
public void clear() { modCount++; final Object[] es = elementData; for (int to = size, i = size = 0; i < to; i++) es[i] = null; }
三、几个问题
一、System.arraycopy()和Arrays.copyOf()方法
联系: 看两者源代码可以发现copyOf()内部调用了System.arraycopy()方法
区别:
-
arraycopy()需要目标数组,将原数组拷贝到你自己定义的数组里,而且可以选择拷贝的起点和长度以及放入新数组中的位置
-
copyOf()是系统自动在内部新建一个数组,并返回该数组。
二、ArrayList的交集,差集,并集,去重并集
-
交集:
list2.retainAll(list1)
,此时list2
中只包含之前list1
与list2
共有的元素(交集),如果lis2
中的元素有被修改过,会返回true
,否则返回false
-
差集:
list2.removeAll(list1);
,此时list2
中只包含之前属于list2
而不属于list1
的元素,如果lis2
中的元素有被修改过,会返回true
,否则返回false
-
并集:
list2.addAll(list1);
,此时list2
中包含之前list1
与list2
中的元素,如果lis2
中的元素有被修改过,会返回true
,否则返回false
- 关于ArrayList的一些源码分析
- CopyOnWriteArrayList 并发集合源码分析
- jdk源码分析之ArrayList
- CopyOnWriteArrayList实现原理及源码分析
- Java容器类源码-ArrayList的最全的源码分析
- Java学习笔记十八-ArrayList源码分析
- ArrayList源码分析
- java源码分析(13)-Arraylist
- java集合03--ArrayList源码分析
- java中List接口的实现类 ArrayList,LinkedList,Vector 的区别 list实现类源码分析
- Java集合源码分析系列-(一)ArrayList源码剖析
- Java数据结构源码分析-ArrayList
- ArrayList源码分析
- Java 容器源码分析之 ArrayList
- Java源码分析-ArrayList
- [Java代码] Java ArrayList源码分析
- List之ArrayList源码分析
- ArrayList源码分析
- Java容器深入研究(jdk 1.8)--- ArrayList总结与源码分析
- java源码分析之List接口以及ArrayList、LinkedList、Stack、Vector等实现类