ArrayList详解及源码分析
一、ArrayList继承及实现体系
二、属性介绍:
ArrayList内部维护了一个Object类型的数组,数组有角标,特点就是查询快,但是增删就相对来说慢一些,所以,如果对数据查询的多,增删的少就用ArrayList来维护数据。
//默认初始容量
private static final int DEFAULT_CAPACITY = 10;
//给指定容量的ArrayList一个空的对象数组
private static final Object[] EMPTY_ELEMENTDATA = {};
//给默认容量的ArrayList一个空的对象数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
对象数组
transient Object[] elementData; // non-private to simplify nested class access
//容量大小
private int size;
//最大容量2的31次方-1-8
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
三、初始化方法介绍:
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
-------------------------------------------------------------------------
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
-----------------------------------------------------------------------
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
之后所有对ArrayList的操作都是对elementData的操作,所以初始化方法主要是针对elementData来进行初始化
1.public ArrayList(int initialCapacity)
当初始化容量大于0时——new 一个新的对象数组,大小为initialCapacity,赋给属性elementData;
当初始化容量等于0时——将属性EMPTY_ELEMENTDATA赋给属性elementData
2.public ArrayList()
无参的构造函数,将属性DEFAULTCAPACITY_EMPTY_ELEMENTDATA赋给属性elementData
3.public ArrayList(Collection<? extends E> c)
将传入的collection类型的集合赋给属性elementData
三、增删方法
ArrayList的方法都是线程不安全的方法,即当一个人对ArrayList中进行增加,另一个人对ArrayList进行修改时,则会出现线程安全问题
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
------------------------------------------------------------------
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
1.boolean add(E e);
①首先,调用ensureCapacityInternal(int minCapacity)方法
//参数为当前数组的大小size+1
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
②里面调用了calculateCapacity(elementData, minCapacity)方法,
----如果初始化ArrayList时是无参构造,那么该方法就会返回10跟size+1(0+1)中的最大值,10
----如果初始化时已经指定大小或elementData已经有元素了,那么该方法就会返回size+1
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
③然后继续调用ensureExplicitCapacity(int minCapacity)方法:修改次数加一
——如果是往无参构造出来的ArrayList里添加元素,minCapacity为size+1,如果此时size=10,10+1>10,调用grow()方法,进行扩容
——其他情况则判断minCapacity是否比当前elementData(带参构造的ArrayList)的长度大,若大则调用grow()方法,minCapacity对应着size+1,elementData对应着数组缓冲区的大小 (size是真实容量,不等同与elementData的大小!)
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
④grow(int minCapacity)
——在③中第一种情况下,grow方法会将elementData的大小扩充至10
——其他情况:①若扩充之后的大小newCapcity还比添加元素之后的size小,则将elementData扩容至size
②若扩容之后newCapcity比添加元素之后的size大,则扩容至newCapcity
举个例子,若elementData中有10个元素了(size=10),且elementData的长度为10(无参构造出来的,默认长度为10),当调用add(“xxx”)方法时,走到grow(11),newCapcity=10+10>>1=10+5=15,但15-11>0,则扩容至15
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
⑤ elementData[size++] = e;将元素添加至末尾
- add(int index, E element)
①rangeCheckForAdd(int index),检查插入位置是否合法
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
②ensureCapacityInternal(size + 1);——操作次数+1,确保缓冲区容量够加入元素
③System.arraycopy(elementData, index, elementData, index + 1,size - index);——重新复制一份elementData,并给新加入的元素留个位置
④elementData[index] = element;——赋值
⑤size++;——size+1
ArrayList最重要的就是扩容机制,理解了扩容机制,其他的方法都不是很复杂
对于ArrayList跟vector来说,他们对内部数组的操作方法都是通过复制数组来完成的,而这就是它们跟LinkedList对于增删操作效率较低的原因