类定义
//出现于jdk1.2,继承于AbstractList,实现了list接口,可克隆,可序列化,支持快速随机访问
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable{}
变量
//默认初始容积
private static final int DEFAULT_CAPACITY = 10;
//第一次添加元素时知道该 elementData 从空的构造函数还是有参构造函数被初始化的,以便确认如何扩容
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//存放元素的数组
transient Object[] elementData;
//数组中元素个数
private int size;
//数组最大长度
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
3个构造器
//自定义数组长度
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
//创建一个长度为传入参数的object数组
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;
}
//将Collection集合作为参数传递进来
public ArrayList(Collection<? extends E> c) {
//将传递进来的集合参数转化为数组并复制给我elementData
elementData = c.toArray();
if ((size = elementData.length) != 0) {
//判断 elementData 的 class 类型是否为 Object[],不是的话将其转换为Object[]
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
//数组长度为0,说明传递进来的集合为空集合,把elementData赋为空的Object数组
this.elementData = EMPTY_ELEMENTDATA;
}
}
核心方法
1、add
ArrsyList有四种添加元素的方法:
- public boolean add(E e) 向数组末尾添加元素
- public void add(int index, E element) 向指定位置添加元素
- public boolean addAll(Collection<? extends E> c) 将一个Collection集合的所有元素添加到集合中
- public boolean addAll(int index, Collection<? extends E> c) 将一个Collection集合的所有元素添加到集合中指定位置
1.向数组末尾添加元素
//添加成功返回true,否则抛异常
public boolean add(E e) {
//判断数组是否需要扩容,需要就扩容,不需要就直接添加,这里的size加1,就是数组所需要的最小容积
ensureCapacityInternal(size + 1);
//完成数组扩容后后将元素赋值到数组对应位置上
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//获取数组的容积
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//如果数组没有初始化,则数组的容积为常量10,否则所需要的容积就是最小容积
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
//判断数组是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {
//首先修改次数加1
modCount++;
//判断所需要的容积是否大于数组现有长度,如果大于,则数组需要扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//参数为数组所需最小长度
private void grow(int minCapacity) {
//获取旧数组的长度
int oldCapacity = elementData.length;
//新数组长度为就数组的1.5倍
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);
}
2.向指定位置添加元素
public void add(int index, E element) {
//判断索引是否合理
rangeCheckForAdd(index);
//判断数组是否需要扩容,需要就扩容,不需要就直接添加,该过程同同第一种添加方式
ensureCapacityInternal(size + 1);
//将元素添指定位置的元素及其后续元素向后移动一位,
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//将元素添加到指定位置
elementData[index] = element;
//最后集合中元素个数加1
size++;
}
//判断索引是否合理
private void rangeCheckForAdd(int index) {
//如果索引不合理则抛异常
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
3.将一个Collection集合的所有元素添加到集合中
public boolean addAll(Collection<? extends E> c) {
//将传递进来的集合转换为Object[]数组
Object[] a = c.toArray();
//获取数组长度
int numNew = a.length;
//判断集合中数组elementData是否需要扩容,需要就扩容,不需要就直接添加,该过程同第一种添加方式
ensureCapacityInternal(size + numNew);
//将a数组拷贝到集合中的数组elementData
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
//如果参数集合为空,则返回为false,添加失败
return numNew != 0;
}
4.将一个Collection集合的所有元素添加到集合中指定位置
//将元素添指定位置的元素及其后续元素向后移动一位,
public boolean addAll(int index, Collection<? extends E> c) {
//判断索引是否合理
rangeCheckForAdd(index);
//将传递进来的集合转换为Object[]数组
Object[] a = c.toArray();
int numNew = a.length;
//判断集合中数组elementData是否需要扩容,需要就扩容,不需要就直接添加,该过程同第一种添加方式
ensureCapacityInternal(size + numNew);
//numMoved为要移动的元素个数
int numMoved = size - index;
if (numMoved > 0)
//将元素添指定位置的元素及其后续元素向后移动numNew位,
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
//将a数组拷贝到elementData数组中,拷贝的起始位置为inedx
System.arraycopy(a, 0, elementData, index, numNew);
//集合中元素个数加number
size += numNew;
return numNew != 0;
}
//判断索引是否合理
private void rangeCheckForAdd(int index) {
//如果索引不合理则抛异常
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
2、remove
- public boolean remove(Object o) 移除指定元素
- public E remove(int index) 移除指定位置元素
- public boolean removeAll(Collection<?> c) 移除此 collection 中那些也包含在指定 collection 中的所有元素
1、移除集合中第一次出现的指定元素,会有两种情况,一种为要移除的元素为null,一种不为null
//移除指定元素
public boolean remove(Object o) {
//不关要移除的元素是否为空,都是先找到元素在数组上的下标,然后调用fastRemove(index)
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;
}
}
false;
}
private void fastRemove(int index) {
//修改次数加1
modCount++;
//因为移除元素会导致该元素后面的元素向前移动,所以需要计算要移动的元素个数
int numMoved = size - index - 1;
//如果要移动的元素个数小于0,说明该元素在数组末尾,直接赋空,并将集合中的元素个数减一
if (numMoved > 0)
//如果要移动的元素个数大于0,则移动元素,最后会多出来一个重复元素,把这个元素赋空,并将集合中的元素个数减一
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null;
}
2、移除指定位置元素
//移除指定位置元素
public E remove(int index) {
//如果传入的下标大于集合中元素个数,则报异常
rangeCheck(index);
//修改次数加1
modCount++;
//获取就数组原有位置的元素
E oldValue = elementData(index);
//获取需要移动的元素个数
int numMoved = size - index - 1;
//如果要移动的元素个数小于0,说明该元素在数组末尾,直接赋空,并将集合中的元素个数减一
if (numMoved > 0)
//则移动元素,最后会多出来一个重复元素,把这个元素赋空,并将集合中的元素个数减一
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null;
//返回被移除的元素
return oldValue;
}
3、移除此 collection 中那些也包含在指定 collection 中的所有元素。此调用返回后,collection 中将不包含任何与指定 collection 相同的元素。
public boolean removeAll(Collection<?> c) {
//判断传递进来的集合是否为空,为空则抛异常
Objects.requireNonNull(c);
return batchRemove(c, false);
}
//判断传递进来的集合是否为空,为空则抛异常
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
private boolean batchRemove(Collection<?> c, boolean complement) {
//将数组中的元素拷贝一份
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
//遍历elementData集合,寻找相同元素
for (; r < size; r++)
//判断集合中是否存在该元素,有则表示找到了相同元素
if (c.contains(elementData[r]) == complement)
//将“不要”删除的元素引用到w下标上,并对w++
//将集合中的数组锁包含的目标集合的元素拷贝出来,填入拷贝的elementData中
elementData[w++] = elementData[r];
} finally {
//发生了异常
if (r != size) {
//复制数组,从报错下标开始复制到 w下标(最后被修改的下标),复制长度是未成功循环的长度
System.arraycopy(elementData, r,
elementData, w,
size - r);
//因为复制后数组长度就变了,所以需要求出目前数组的长度,w+ 复制的长度
w += size - r;
}
//不相等,则表示有相同的元素,w为相同的元素个数
if (w != size) {
//从w开始循环,循环到size,这些数据是要删除的数据,设为null
for (int i = w; i < size; i++)
//将所有需要移除的元素设置为null。
elementData[i] = null;
//修改操作次数计数 size-w表示删除的数量
modCount += size - w;
//将size的大小设置为w
size = w;
//成功标识设为true
modified = true;
}
}
//最后返回移标识
return modified;
}
//判断集合中是否存在该元素
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
//获取元素第一次出现在集合中的下标,从前向后找
public int indexOf(Object o) {
if (o == null) {
//如果传入的元素为空,采用双等号判定
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
//如果不为空,采用equals方法进行比较
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
//没有找到返回-1
return -1;
}
3、get与set
public E get(int index) {
//如果传入的下标大于集合中元素个数,则报异常
rangeCheck(index);
//返回指定下标的元素
return elementData(index);
}
public E set(int index, E element) {
//如果传入的下标大于集合中元素个数,则报异常
rangeCheck(index);
//获取下标位置的元素
E oldValue = elementData(index);
//覆盖掉之前位置的元素
elementData[index] = element;
//返回被替换的值
return oldValue;
}
4、clear
public void clear() {
modCount++;
//将所有元素赋空
for (int i = 0; i < size; i++)
elementData[i] = null;
//集合中元素个数修改为0
size = 0;
}
//如果传入的下标大于集合中元素个数,则报异常
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
其他常用方法
//返回集合中元素个数
public int size() {
return size;
}
//判断集合是否为空
public boolean isEmpty() {
return size == 0;
}
//判断集合中是否存在改元素
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
//获取元素最后一次出现在集合中的下标,从后向前找
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
//将集合转化为Object[]
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
//浅克隆
public Object clone() {
try {
//
ArrayList<?> v = (ArrayList<?>) super.clone();
//将对象的数组拷贝一份
v.elementData = Arrays.copyOf(elementData, size);
//将修改次数设置为0
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
//如果新数组长度大于许所允许的最大容积
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}