ArrayList & LinkedList & Vector源码分析
1. ArrayList源码分析
1.1ArrayList继承关系和层次结构
- ArrayList:说明支持了泛型
- entends AbstractList:继承了AbstractList
- implements List, RandomAccess, Cloneable, java.io.Serializable
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
private static final long serialVersionUID = 8683452581122892189L;
//默认的初始化容量
private static final int DEFAULT_CAPACITY = 10;
// 指定该ArrayList容量为0时,返回该空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
//当调用无参构造方法,返回的是该数组。刚创建一个ArrayList时,其内数据为0
//它与EMPTY_ELEMENTDATA的区别就是:该数组是默认返回的,而EMPTY_ELEMENTDATA是在用户指定容量为0时返回。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//保存添加到ArrayList中的元素
//ArrayList的容量就是该数组的长度
//该值为DEFAULTCAPACITY_EMPTY_ELEMENTDATA时,当第一次添加元素进入ArrayList中时,数组将扩容值DEFAULT_CAPACITY
//被标记为transient
transient Object[] elementData; // non-private to simplify nested class access
//ArrayList的实际大小(数组包含的元素个数/实际数据的数量)默认为0;
private int size;
1.2构造方法
- ArrayList提供3中构造方法:
- ArrayList(int initialCapacity):构造一个指定容量为capacity的空ArrayList,这是一个带初始容量大小的有参构造函数
- 初始容量大于0,实例化数组,将自定义的容量大小当成初始化elementData的大小
- 初始容量等于0,将空数组赋值给elementData
- 初始容量小于0,将抛出异常
//构造具有指定初始容量的空list
public ArrayList(int initialCapacity) {//传入容量大小的参数
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];//如果比0大就创建传入参数大小的容量数组
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA; //如果相等赋值为空数组
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);//穿入负数抛出异常
}
}
- ArrayList() :构造一个初始容量为10的空列表。如果创建ArrayList对象时不传入参数,则使用此无参构造方法创建ArrayList对象。从前面可以知道DEFAULTCAPACITY_EMPTY_ELEMENTDATA是一个空的Object[],将elementData初始化,elementData也是Object[]类型。空的Object[]会给默认的容量,但是这里我们并没有看到数组容量变成10,那么是什么时候回被初始化为10的数组呢?答案是有元素被加入时(使用add())。当第一次使用add方法时候,elementData将会变成默认长度:10.
//构造一个初始容量为10的空列表
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;//
}
- ArrayList(Collection<? extends E> c) :(暂时没有搞懂)
public ArrayList(Collection<? extends E> c) {
Object[] a = c.toArray();
if ((size = a.length) != 0) {
if (c.getClass() == ArrayList.class) {
elementData = a;
} else {
elementData = Arrays.copyOf(a, size, Object[].class);
}
} else {
// replace with empty array.
elementData = EMPTY_ELEMENTDATA;
}
}
1.3主要方法
- add(e):
//将指定的元素追加到此列表的末尾
public boolean add(E e) {
//确定list容量,如果不够容量加1.注意:只加1,保证资源不被浪费
ensureCapacityInternal(size + 1); // Increments modCount!!
//将元素e放到size的索引位置上,并size++
elementData[size++] = e;
return true;
}
//数组容量检查,不够时则进行扩容,只供内部类使用
private void ensureCapacityInternal(int minCapacity) { //传入的是size+1的值
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);//三维运算:true时取DEFAULT_CAPACITY;false取minCapacity
//若elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA,则取minCapacity为DEFAULT_CAPACITY和参数minCapacity之间最大的值。
//DEFAULT_CAPACITY在此之前初始化容量为10
}
return minCapacity;
}
//数组容量检查,不够时则进行扩容,只供内部类使用
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
//最小容量>数组缓冲区当前长度
if (minCapacity - elementData.length > 0)
grow(minCapacity);//扩容
}
-
add 方法是在list的末尾添加一个元素。size是数组中数据的个数,因为要添加一个元素,所以size+1,先判断size+1的这个个数数组能否放得下。calculateCapacity方法用来计算容量。判断初始化的elementData是不是空的数组,如果是空数组长度size就是0,否则数组长度就不是0。
- 如果elementData元素是空的,就是第一次添加元素,minCapacity=size+1,其实就是等于1,空的数组没有长度就存放不了,所以就将minCapacity变成10,也就是默认容量的大小。也就是elementData数组的此时需要的最小容量为变10了,此时ensureExplicitCapacity()方法中的minCapacity就是10。到此只是说elementData数组需要的容量至少是10,还没有改变elementData的大小。
- 如果elementData数组中的元素不是空的,那么它此时需要的最小容量就是原先的数组长度加1,minCapacity代表着elementData中元素增加之后的实际数据个数。
- 要始终记住minCapacity = size+1,在ensureExplicitCapacity()方法中,首先操作数自增1,再把需要的最小空间容量与数组当前实际长度进行比较:
- 如果elementData中的元素是空的,它现在需要的容量是10,但是elementData.length为0,所以要扩容。
- 如果elementData数组中的元素不是空的,若它添加一个元素后需要的容量比原数组长度大,就需要扩容,否则就不需要扩容。
-
ArrayList能自动扩展大小的关键方法就在下面的grow()方法里:
/**
*扩容,保证ArrayList至少能存储minCapacity个元素
*第一次扩容,逻辑为newCapacity=oldCapacity + (oldCapacity >> 1);即在原有容量基础上增加一半。
*第一次扩容后,如果容量还小于minCapacity,就将容量扩充为minCapacity。
*
*/
private void grow(int minCapacity) {
// overflow-conscious code
//获取当前数组的容量
int oldCapacity = elementData.length;
//扩容:新的容量 = 当前容量+(当前容量/2)。即在原有容量基础上增加一半(当前容量的1.5倍)
int newCapacity = oldCapacity + (oldCapacity >> 1);
//如果扩容后容量还是小于想要的最想容量
if (newCapacity - minCapacity < 0)
//将扩容后容量再次扩容为想要的最小容量
newCapacity = minCapacity;
//elementData为空数组的时候,length=0,oldCapacity=0,newCapacity,在这里就是真正的初始化elementData的大小:10
//如果扩容后的容量大于临界点免责进行大容量分配
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
//新的容量确认好了,就copy数组,改变容量大小
//copyof(原数组,新的数组长度)
elementData = Arrays.copyOf(elementData, newCapacity);
}
//进行容量分配
private static int hugeCapacity(int minCapacity) {
//minCapacity < 0 抛出异常
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
//如果想要的容量大于MAX_ARRAY_SIZE,则分配 Integer.MAX_VALUE,反则分配AX_ARRAY_SIZE
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
AX_ARRAY_SIZE;
}
2.LinkedList源码分析
2.1LinkedList继承关系
- LinkedList(E:支持泛型):它继承了AbstractSequentialList类.
- 实现接口:List、Deque、Cloneable、java.io.Serializable
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable{}
2.2数据结构:
//元素个数(节点个数)
transient int size = 0;
//节点头
transient Node<E> first;
//节点尾
transient Node<E> last;
//私有静态内部类,表示双向链表节点
private static class Node<E> {
E item; //元素
Node<E> next; //指向后一个节点
Node<E> prev; //指向前一个节点
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
2.3 构造方法:
public LinkedList() {
}
public LinkedList(Collection<? extends E> c) {
this();
//添加c到链表中
addAll(c);
}
2.4.1 add()增加方法理解:
- LinkedListCRUD:CRUD代表着的就是增删改查
public boolean add(E e) { //传入参数
linkLast(e);
return true;
}
void linkLast(E e) { //传入的参数
final Node<E> l = last; //记录尾结点
final Node<E> newNode = new Node<>(l, e, null);//创建新节点(1.上个节点的地址、2.参数、3.null)
last = newNode; //更新尾部节点
if (l == null) //判断相等情况下,更新节点头
first = newNode;
else //否则将新节点的地址赋值给上个节点next位置
l.next = newNode;
size++;
modCount++;
}
- 使用add方法,传入参数。
- 记录尾节点。
- 创建新节点(节点参数:l:上个节点的地址、e:实参、null)
- 更新尾部节点。
- 判断列表为空时:更新头节点,first指向新的节点 / 判断列表不为空时:将新节点的地址赋值给上个节点next位置。
2.4.1.11图片分析:
2.4.2 remove()删除方法
//1.默认的删除方法,它删除的是第一个元素,调用removeFirst()方法
public E remove() {
return removeFirst();
}
//2.执行removeFirst()
public E removeFirst() {
final Node<E> f = first;//f指向first(第一个元素)
if (f == null) //判断为空时,抛出异常
throw new NoSuchElementException();
return unlinkFirst(f); //否则使用unlinkFirst方法
}
private E unlinkFirst(Node<E> f) { //unlinkfirst()方法才是关键的地方
// assert f == first && f != null;
final E element = f.item; //将第一个元素赋值给elment
final Node<E> next = f.next; //将第一个元素的next指向后面一个元素
f.item = null; //将第一个元素赋值为空
f.next = null; // help GC //将第一个元素next也赋值为空,让系统以为它是产生的垃圾
first = next; //让first指向后面一位元素
if (next == null)
last = null; //如果为空,last为null
else
next.prev = null; //如果不为空,后一位元素前节点的地址为空,那新的第一位元素和被删除的第一位链表都断了(不指向)
size--; //size-1
modCount++;
return element;
}
2.4.2.1图片分析
3.Vector源码分析
3.1继承关系
- extends AbstractList:继承 AbstractLis类
- implements List, RandomAccess, Cloneable, java.io.Serializable:实现接口
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
3.2成员变量
protected Object[] elementData;//空的数组
protected int elementCount; //记录数组中元素的个数
protected int capacityIncrement;//扩容大小
3.3构造方法
//有参构造
public Vector(int initialCapacity, int capacityIncrement) {//传入传数
super();
if (initialCapacity < 0) //判断传入元素小于0,抛出异常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];//创建为一个输入多少参数长度的数组
this.capacityIncrement = capacityIncrement;//如果不去动,它会一直为0
//有参构造
public Vector(int initialCapacity) {//传入的参数
this(initialCapacity, 0);//this方法:public Vector(int initialCapacity, int capacityIncrement) {就是上面的有参构造
}
//无参构造
public Vector() {
this(10);//这里的this调用的是Vector(int initialCapacity) 方法
}
public Vector(Collection<? extends E> c) {
Object[] a = c.toArray();
elementCount = a.length;
if (c.getClass() == ArrayList.class) {
elementData = a;
} else {
elementData = Arrays.copyOf(a, elementCount, Object[].class);
}
}
3.4解读add方法源码
public synchronized boolean add(E e) {//传入的实参元素
modCount++; //修改次数
ensureCapacityHelper(elementCount + 1);//
elementData[elementCount++] = e;
return true;
}
private void ensureCapacityHelper(int minCapacity) {//minCapacity:elementCount + 1,
if (minCapacity - elementData.length > 0) //当判断为true时,使用grow方法(扩容),反则不扩容
grow(minCapacity);
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
int oldCapacity = elementData.length; //数组当前大小赋值给old
//old+(这边是一个三元运算为true时,取capacityIncrement:0;false时取,oldCapacity)
//当三元运算为false时,就是oldCapacity+oldCapacity,相当于2倍扩容
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)//当这边的判断成立时,将minCapacity的值赋值给newCapacity
//false时,直接执行Arrays.copyOf 按2倍扩容
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);//原数组,扩容的大小(按2倍扩容)
}