ArrayList的学习
1.成员变量
private static final int DEFAULT_CAPACITY = 10;
//设置初始容量为10
private static final Object[] EMPTY_ELEMENTDATA = {};
//设置空对象数组,用于在new ArrayList()时使用
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//空数组,在添加第一个数组的时候变成默认容量的数组
transient Object[] elementData;
//由于有transient关键字修饰,其不会序列化。元素数组也即真实存放数据的数组。
private int size;
//用于帮助记录创建的ArrayList的大小,只需要在调用add()方法的时候使用size++就可以清楚的记录size。也就是Arraylist的大小
2构造方法
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);
}
}
//传入的数大于等于0则创建ArayList对象并且初始化,且拥有初始容量,如果都不是则抛出。
//即为创建一个指定容量的ArrayList
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//利用无参构造创建创建ArrayLis对象,且创建的数组的容量为初始值
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
if (elementData.getClass() != Object[].class)
//getClass()返回运行时的类;如果c是ArrayList的对象就直接将引用赋给elementData(真实存储数据的数组)
elementData = Arrays.copyOf(elementData, size, Object[].class);
//Arraycopy将旧数组按指定大小copy到新数组,用于达到扩容的目的
} else {
this.elementData = EMPTY_ELEMENTDATA;
}
}
/*
1.将参数中的集合转化为数组赋给elementData;
2.判断Collection是否是空。通过比较size与第一步中的数组长度的大小。
3.如果Collection为空,则设置元素数组为空,即将EMPTY_ELEMENTDATA赋给elementData;
4.如果Collection不为空,接下来判断是否成功将Collection转化为Object类型的数组,如果转化成Object类型的数组成功,则将数组进行复制,转化为Object类型的数组*/
3.成员方法
3.1. ensureCapacity()方法
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)? 0 : DEFAULT_CAPACITY;
//判断elementDate的大小是否是默认值,不是则返回0,是则返回默认大小
//由于传过来的 minCapacity = size + 1 ,若minExpand = 0因为size默认大小为0,则minCapacity一定大于minExpand
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
//判断当前数组是否是默认构造方法生成的空数组,如果是的话minCapacity=10反之则根据原来的值传入下一个方法去完成下一步的扩容判断
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//判断当前ArrayList是否扩容,如果扩容了则调用grow()方法
3.2.grow()方法
private void grow(int minCapacity) {
int oldCapacity = elementData.length; //旧容量
int newCapacity = oldCapacity + (oldCapacity >> 1); //新容量为旧容量的1.5倍
if (newCapacity - minCapacity < 0) //新容量小于参数指定容量,修改新容量
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0) //新容量大于最大容量
newCapacity = hugeCapacity(minCapacity); //指定新容量
// copy扩容
elementData = Arrays.copyOf(elementData, newCapacity);
}
//ArrayList中最重要的方法之一,grow()方法用于ArrayList的扩容
//每次扩容扩容MAX(oldCapacity + (oldCapacity >> 1),minCapacity)
附:ArrayList的扩容机制了解
ArrayList的扩容主要发生在向ArrayList集合中添加元素的时候。即调用add()方法时
主要经历了以下几个阶段:
-
在add()方法中调用ensureCapacityInternal(size + 1)方法即ensureCapacityInternal(int minCapacity)来确定集合确保添加元素成功的最小集合容量minCapacity的值。参数为size+1,代表的含义是如果集合添加元素成功后,集合中的实际元素个数。换句话说,集合为了确保添加元素成功,那么集合的最小容量minCapacity应该是size+1。在ensureCapacityInternal方法中,首先判断elementData是否为默认的空数组,如果是,minCapacity为minCapacity与集合默认容量大小中的较大值。
-
调用ensureExplicitCapacity(minCapacity)方法来确定集合为了确保添加元素成功是否需要对现有的元素数组进行扩容。首先将结构性修改计数器加一;然后判断minCapacity与当前元素数组的长度的大小,如果minCapacity比当前元素数组的长度的大小大的时候需要扩容,进入第三阶段。
-
如果需要对现有的元素数组进行扩容,则调用grow(minCapacity)方法,参数minCapacity表示集合为了确保添加元素成功的最小容量。在扩容的时候,对扩容后的容量与minCapacity进行比较:
- 新容量小于minCapacity,则将新容量设为minCapacity;
2.新容量大于minCapacity,则指定新容量。最后将旧数组拷贝到扩容后的新数组中。
用到了如下方法:
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
对于list来说最重要的便是增·删·查·改
3.3.add()方法–增
public boolean add(E e) {
ensureCapacityInternal(size + 1);
// 调用ensureCapacityInternal(int minCapacity)方法来判定是佛需要扩容
elementData[size++] = e;
return true;
}
/*
1.确保能够添加元素,并且判断是否需要扩容
2.将元素添加到elementData的相应位置
3.将size+1
*/
public void add(int index, E element) {
rangeCheckForAdd(index);
nsureCapacityInternal(size + 1); //对数组扩容
System.arraycopy(elementData, index, elementData, index + 1,
size - index);//从index下标开始,将后面的元素全部拷贝到index+1之后
elementData[index] = element;//将index下标的值赋为element
size++;
}
//此方法能够将元素e添加到数组中下标为index处
3.4.set()方法–改
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
//此方法可以在指定下标index处添加指定元素e
3.5.clear()方法–删
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
//通过for循环将数组elementData清零,并将用于记录其大小的size清零
3.6.get()方法–查
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
//检查给定的索引是否在范围内。如果不是,则引发适当的运行时异常。
E elementData(int index) {
return (E) elementData[index];
}
//用这个方法来获得指定下标的元素
//以上两个方法结合便成了get()方法
3.7.size()方法
public int size() {
return size;
}
//返回数组的长度
3.8.remove()方法
public E remove(int index) {
// 检查索引是否合法
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
// 需要移动的元素的个数
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 赋值为空
elementData[--size] = null;
// 返回旧值
return oldValue;
}
//作用是删除指定下标的元素,并且删除后数组会向前移动size - index - 1个位置,并且将数组最后一位设置为null方便GC
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;
}
/*从此列表中删除第一次出现的指定元素(如果存在)。
如果列表不包含该元素,则它不变。
如果此列表包含指定的元素返回 true。*/