一、首先看看ArrayList 的基类有哪些java
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable数组
能够总结为:dom
ArrayList 继承了AbstractList 函数
实现了 List 、 RondomAcess 、 cloneable 、 serializable 对于通常的容器都会实现cloneable 用于克隆 实现serializable 实现序列化this
二、在ArrayList中定义的变量spa
主要有4个变量,在了解这些变量以前咱们须要知道一些基本信息,ArrayList 底层用的是数组实现的,初始的默认容量是10 ,code
private static final int DEFAULT_CAPACITY = 10;//初始的默认容量对象
private static final Object[] EMPTY_ELEMENTDATA = {}; //空数组继承
private transient Object[] elementData;//ArrayList 中真正存数据的容器ci
private int size;//当前数组中的数据个数
三、ArrayList中的构造函数
ArrayList中总共有三个构造函数,分别是有初始化容量的构造函数,没有参数的构造函数,带有集合参数的构造函数
(1) 从源码中咱们能够看到这只是一个很简单的 建立数组对象的过程,不过在建立对象以前须要对初始容量值判断是否<0
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
(2)无参的构造函数,这里咱们须要注意的是虽然数组默认容量是10 ,可是无参的状况下数组的初始化实际是个空数组而不是建立大小为10的数组
public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;
}
(3)集合 由于是对数组操做因此 用的是Arrays.copyOf 进行复制和类型转换
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
四、主要函数的分析 对于容器通常咱们经常使用的操做是增删改查
增
(1)增长一个数的操做,size记录当前数组中的数个数因此实际操做是在数组下标为size位置赋值,而后 size++ ,可是在这以前得判断size+1是否越界,也就是是否须要进行扩容,扩容咱们下面再讲,该函数的返回值是Boolean类型
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
(2)在指定的下标增长一个数的操做。在这里咱们须要注意一个函数就是System.arraycopy()这个是ArrayList中经常使用的API,主要用于数组移位
原先的数存在elementData[]中,一共size个对象,如今咱们须要在下标为index插入新对象 就须要咱们将 下标[index ,size-1]范围的对象移位到[index+1,size]
操做是 System.arraycopy(elementData, index, elementData, index + 1,size - index);
(源数组 ,复制开始下标,目标数组,目标数组开始下标,复制对象的个数)
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++;
}
(3)增长一个集合的操做。首先将集合转换为数组,再判断假如该集合数目的对象是否须要扩容,再调用System.arraycopy函数将新数组中的数复制到elementData[]中
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
删
(1)删除下标为index的对象,先判断index是否有效,而后是向前移一位复制,最后size下标位置赋值为null,返回删除的对象
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);//这里可能会疑惑为何是elementData(index)而不是elementData[index] 由于elementData(index)是ArrayList的一个内部函数,实际也是返回下标为index的对象。
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,numMoved);// 又出现了System.arraycopy。。。。当别人问我ArrayList中对什么印象最深入那么就是这个了
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
(2)删除某个对象操做。实际上这是个遍历数组顺便比较的过程,分两个条件去遍历,当删除的对象是 null的状况可非null的状况下进行,从中咱们能够看出实际上在遇到第一个符合条件的对象就返回了,因此这个操做并不删除完全部
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
(3) 删除一个集合的操做。
public boolean removeAll(Collection<?> c) {
return batchRemove(c, false);// 调用一个内部函数
}
//这个函数利用了tow poit 的思想,一个读节点,一个写节点,遍历数组当该对象不存在集合中时保留下来,最后对[w,size]范围赋值为null
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
if (r != size) {
System.arraycopy(elementData, r,elementData, w,size - r);
w += size - r;
}
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
改
(1) 对index下标处的对象赋值新的对象,首先判断index是否有限,而后对数组下标为index位置赋值,返回旧对象
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
查
(1)查找下标为index 的对象 ,先判断 index是否有效,再返回下标为index的数组
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
扩容分析
上面咱们在分析增长一个对象操做以前会调用 ensureCapacityInternal(size + 1) ,如今咱们将沿着这条线去分析扩容的过程
private void ensureCapacityInternal(int minCapacity) {
if (elementData == EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);//当当前数组为空时取默认容量与最小容量的最大值
}
ensureExplicitCapacity(minCapacity);//用来比较当前的数组长度和最小容量
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)//当须要的最小容量大于当前数组的长度时须要进行扩容
grow(minCapacity);
}
//扩容函数
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); //这里能够看出扩容的容量是旧容量的1.5倍
if (newCapacity - minCapacity < 0)//还要将1.5倍旧容量与最小须要的容量比较取较大值
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);//建立新newCapacity的长度为数组将旧数组的值复值给新数组
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE; //从这里能够看出数组可以容许的最大扩容大小为Integer.MAX_VALUE
}
其余经常使用的函数
一、判空函数
public boolean isEmpty() {
return size == 0;
}
二、查当前ArrayList中的对象个数
public int size() {
return size;
}
三、是否含有某个对象
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
//一样是分为null 和非 null 去遍历
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
四、将容量缩小 , 减小空间
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = Arrays.copyOf(elementData, size); //建立size大小的新数组,将旧数组的值赋值到新数组
}
}
总结: ArrayList 底层用的是数组实现,默认初始化容量为10 ,扩容规则为旧数组长度的1.5倍与当前须要的最小容量的最大值,最经常使用的操做是Arrays.copyOf() 和 System.arraycopy()