ArrayList简介
ArrayList是一个数组队列,相当于动态数组。与Java中的数组相比,他的容量能动态增长。它继承于AbstractList,实现了List,RandomAccess,Cloneable,java.io.Serializable这些接口。
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable {
//ArrayList继承了AbstractList,实现了List。它是一个数组队列,提供了相关的添加、删除、修遍历等功能。
//ArrayList实现了RandomAccess接口,即提供了随机访问机制。RandomAccess是java中用来被List实现,为List提供快速访问功能的。再ArrayList中,我们可以通过元素的序号快速获取元素对象,这就是快速访问机制。
//ArrList实现了Cloneable接口,这意味着覆盖了函数clone(),能被克隆
//ArrayList实现了java.io.Serializable接口,说明ArrayList支持序列化,能够通过序列化进行传输
//ArrrayList不是线程安全的,建议再单线程中使用,多线程可以使用Vector或者CopyOnWritrArrayList
}
ArrayList属性
//实现序列化接口时需要有序列化id
private static final long serialVersionUID = 8683452581122892189L;
//默认初始的容量
private static final int DEFAULT_CAPACITY = 10;
//一个空对象
private static final Object[] EMPTY_ELEMENTDATA = new Object[0];
//一个空对象,如果使用默认构造函数创建,则默认对象内容是该值
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = new Object[0];
//当前数据对象存放的地方,当前对象不参与序列化
transient Object[] elementData;
//当前数组大小
private int size;
//数组最大长度
private static final int MAX_ARRAY_SIZE = 2147483639;
ArrayList构造方法
//有参构造 elementData是用于存放数组
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
//初始值大于0,创建一个初始值大小的Object数组
this.elementData = new Object[initialCapacity];
} else {
//数组小于0抛出异常
if (initialCapacity != 0) {
throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
}
//等于0,则为空对象
this.elementData = EMPTY_ELEMENTDATA;
}
}
//无参构造
public ArrayList() {
//如果不传入参数,创建一个空对象 = new Object[0]
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
//注意:此时我们创建的ArrayList对象中的elementData中的长度是1,size是0,当进行第一次add
的时候,elementData会变成默认长度:10
}
//带Collection对象的构造函数,按照集合的迭代器返回的顺序构造一个包含指定集合元素的列表。
public ArrayList(Collection<? extends E> c) {
//将集合变量c运用.toArray()转换为数组,后将数组地址赋值给elementData
this.elementData = c.toArray();
//将elementData长度赋值给size,判断size的大小
if ((this.size = this.elementData.length) != 0) {
//this.elementData = arg0.toArray(),这里执行的简单赋值时浅拷贝,所以要执行深拷贝
if (this.elementData.getClass() != Object[].class) {
this.elementData = Arrays.copyOf(this.elementData, this.size,
Object[].class);
}
} else {
//等于0直接将空对象的地址赋值给elementData
this.elementData = EMPTY_ELEMENTDATA;
}
}
add方法
jdk1.8版本:add方法有两个,一个是带一个参数的,一个是带两个参数的
add(E e)方法
添加元素方法入口
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
确保添加的元素有地方存储,当第一次添加元素的时候this.size+1的值是1,所以第一次添加的时候会将当前elementData数组的长度变为10
private void ensureCapacityInternal(int minCapacity) {
//判断是否相同,因为第一次elementData等于DEFAULTCAPACITY_EMPTY_ELEMENTDATA
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//第一次添加时DEFAULT_CAPACITY大于,其值为10
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//不是第一次
ensureExplicitCapacity(minCapacity);
}
将修改次数(modCount)自增,判断是否需要扩充数组长度,判断条件就是当前所需的数组最小长度与数组的长度的对比,如果大于零,则增长数组长度。
private void ensureExplicitCapacity(int minCapacity) {
//修改次数自增
modCount++;
// overflow-conscious code
//判断当前所需数组最小长度是否大于数组长度,如果大于,则自增
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
如果当前数组已使用空间(size)加一之后大于数组长度,则增大数组容量,扩大到原来的1.5倍
private void grow(int minCapacity) {
// overflow-conscious code
//之前数组长度
int oldCapacity = elementData.length;
// >>右移位运算符,将一个数的各二进制全部右移若干位,正数左补零,负数左补1,右边丢弃,操
作数每右移一位,相当于该数除以2,所以此时newCapacity为原来oldCapacity的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
//如果newCapacity还是小于minCapacity,赋值
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//新数组是否大于最大数组大小
if (newCapacity - MAX_ARRAY_SIZE > 0)
//调用hugeCapacity方法
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, 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;
}
add(int index,E element)方法
执行逻辑:
#确保这个数插入的位置小于等于当前数组长度,并且不小于0,否则抛出异常
#确保数组已使用长度size+1之后足够存下下一个数据
#修改自增次数,如果当前数组已使用长度size+1后的大于当前的数组长度,则调用grow方法,增长数组
#grow方法会将当前数组的长度变为原来的1.5倍
#确保有足够的容量之后,使用System.arraycopy将需要插入的位置index后面的元素统统后移一位
#将新的数据存放到数组的指定位置index上
public void add(int index, E element) {
//确保index小于等于当前数组长度,并且不小于0,否则抛出异常
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
//确保有足够的容量之后,使用System.arraycopy将需要插入的位置index后面的元素统统后移一位
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//将新的数据内容放到指定位置
elementData[index] = element;
//长度加一
size++;
}
注意:使用该方法的话导致指定位置后面的数组元素全部重新移动,即先后移动一位,这里用到了数据结构的内容。
get方法
返回指定位置上的元素
public E get(int index) {
//index超出范围报异常
rangeCheck(index);
//判断modCount是否相同
checkForComodification();
//返回结果
return ArrayList.this.elementData(offset + index);
}
set方法
确保set位置小于当前数组的长度size并且大于0,获取指定位置index元素,然后放到oldValue存放,将需要设置的元素放到指定的位置index上,然后将原来位置上的元素oldValue返回给用户。
public E set(int index, E element) {
//判断是否出界
rangeCheck(index);
//获取index本来的元素
E oldValue = elementData(index);
//新元素存放到elementData中
elementData[index] = element;
return oldValue;
}
contains方法
调用indexOf方法,遍历数组中的每一个元素作对比,如果找到该元素,返回true否则false
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 {
for (int i = 0; i < size; i++)
//不为空就与目标元素想比较
if (o.equals(elementData[i]))
//返回
return i;
}
//错误返回
return -1;
}
remove方法
根据索引remove
代码逻辑:
#判断索引有没有越界
#自增修改次数
#将指定位置index上的元素保存到oldValue
#将指定位置index上的元素都往前移动一位
#将最后面的一个元素置空,让垃圾回收器回收
#将原来oldValue值返回
注意:置空并不会改变数组长度,只是将最后一个元素置空
public E remove(int index) {
//判断是否越界
rangeCheck(index);
//自增修改次数
modCount++;
//将原来index位置上的元素赋值给oldValue
E oldValue = elementData(index);
//移动的数字
int numMoved = size - index - 1;
if (numMoved > 0)
//数组拷贝,index+1原数组位置,index目标数组位置,numMoved数组长度
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//最后一个元素置空
elementData[--size] = null; // clear to let GC do its work
//返回
return oldValue;
}
根据对象remove
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])) {
//还是根据index来删除的元素
fastRemove(index);
return true;
}
}
return false;
}
clear方法
添加操作次数modCount,将数组内的元素都置空,等待垃圾回收器手机,不减小数组容量
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
sublist方法
- 返回指定的
fromIndex
(包含)和toIndex
(独占)之间的此列表部分的视图。 (如果fromIndex
和toIndex
相等,则返回的列表为空。)返回的列表由此列表支持,因此返回列表中的非结构更改将反映在此列表中,反之亦然。 返回的列表支持所有可选的列表操作。此方法消除了对显式范围操作的需要(对于数组通常存在的排序)。 任何需要列表的操作都可以通过传递subList视图而不是整个列表来用作范围操作。 例如,以下习语从列表中删除了一系列元素: list.subList(from, to).clear(); -
public List<E> subList(int fromIndex, int toIndex) { //判断 subListRangeCheck(fromIndex, toIndex, size); return new SubList(this, 0, fromIndex, toIndex); } SubList(AbstractList<E> parent, int offset, int fromIndex, int toIndex) { this.parent = parent; this.parentOffset = fromIndex; this.offset = offset + fromIndex; this.size = toIndex - fromIndex; this.modCount = ArrayList.this.modCount; }
iterator方法
-
iterator方法返回的的时一个内部类,由于内部类的创建默认含有外部的this,所以这个内部类可以调用到外部类的属性
-
public Iterator<E> iterator() { return listIterator(); }
一般调用完iterator之后,我们会使用iterator做遍历,这里使用next做遍历的时候有个需要注意的地方,就是调用next的时候,可能会发生ConcurrentModificationException,当修改次数与期望的修改次数不一致的时候,会发生该异常。
-
public E next() { checkForComodification(); int i = cursor; if (i >= SubList.this.size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (offset + i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[offset + (lastRet = i)]; }
总结回顾:
-
序列化,反序列化掌握不足
-
泛型
-
迭代器
-
深拷贝,浅拷贝
-
Object类