ArrayList
1、ArrayList的特点
相比之前的LinkedList可以实现动态扩容。
2、成员属性
private static final long serialVersionUID = 8683452581122892189L;
//JAVA序列化的机制是通过判断类的serialVersionUID来验证的版本一致的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID于本地相应实体类的serialVersionUID进行比较。如果相同说明是一致的,可以进行反序列化,否则会出现反序列化版本一致的异常。
private static final int DEFAULT_CAPACITY = 10;
//默认的数组容量值
private static final Object[] EMPTY_ELEMENTDATA = {};
//空的elementData数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//无参构造要使用的空对象数组。
transient Object[] elementData;
//定义一个Object引用类型数组,存放ArrayList的元素。也是ArrayList操作的主要数组。
private int size;
//表示当前ArrayList元素个数,始终小于elementData长度
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
(-8:避免一些机器内存溢出,减少出错几率,所以少分配)
//定义该数组最大容量
3、方法
1、构造方法(三种构造方法)
//无参构造
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;//将定义的空数组赋值给elementData
}
//有参构造
public ArrayList(int initialCapacity)//指定数组容量
{
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];//定义elementData容量
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;//如果指定容量为0则把定义的空数组赋值给elementData
} else //小于0则不满足容量规范,抛出异常并提示非法异常
throw new IllegalArgumentException("IllegalCapacity:"+initialCapacity);
}
}
//还有一种迭代器构造方法(没看懂)
2、动态扩容方法
在每个add方法中会进行一次容量查询,如add中
public boolean add(E e) {
ensureCapacityInternal(size + 1);//进行容量查询
elementData[size++] = e;
return true;
}
//
private void ensureCapacityInternal(int minCapacity)//将数组容量+1作为最小容量
{
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}//当无参构造执行后,第一次调用add方法增加时,执行到这就会将最小容量变为10
return 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);//定义新的数组容量为当前数组容量的1.5倍
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//如果增大后的新的数组容量小于于最小容量则把最小容量作为新的数组容量
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//如果新的数组容量大于该数组最大容量
elementData = Arrays.copyOf(elementData, newCapacity);
//将elementData容量扩容成newCapacity之后返回给elementData
//主要实现是通过Arrays.copyOf()方法
}
//
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
//
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
//
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0)//如果最小容量小于0则抛出异常
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
//如果最小容量大于定义的最大数组容量则返回Integer类型的最大值 否则还是返回Integer最大值2的31次方-1
}
add方法在实现动态扩容后就会将参数赋值给数组的相应位置。其他add方法也都是先对容量进行检查然后拷贝数组。
3、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; // clear to let GC do its work
return oldValue;
}
//
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
当我们调用 remove(int index) 时,首先会检查 index 是否合法,然后再判断要删除的元素是否位于数组的最后一个位置。如果 index 不是最后一个,就再次调用 System.arraycopy() 方法拷贝数组。说白了就是将从 index + 1 开始向后所有的元素都向前挪一个位置。然后将数组的最后一个位置空,size - 1。如果 index 是最后一个元素那么就直接将数组的最后一个位置空,size - 1即可。 当我们调用 remove(Object o) 时,会把 o 分为是否为空来分别处理。然后对数组做遍历,找到第一个与 o 对应的下标 index,然后调用 fastRemove 方法,删除下标为 index 的元素。 fastRemove(int index) 方法和 remove(int index) 方法基本全部相同。
4、clear方法
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
就是将数组的元素逐个赋值为空。
5、toArrat方法
顾名思义就是把list变成一个Object数组
public Object[] toArray() {
return Arrays.copyOf(elementData, size);//copyOf方法
}
第二个方法可以把list变成你需要的类型数组
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;
}
6、其他方法
set方法:
public E set(int index, E element) {
rangeCheck(index);//检查索引是否越界
E oldValue = elementData(index);//将旧值保存
elementData[index] = element;//赋新值
return oldValue;//返回旧值
}
indexOf方法:
// 从首开始查找数组里面是否存在指定元素
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;
}
get方法:
public E get(int index) {
rangeCheck(index);//检查索引是否越界
return elementData(index);
}
//
E elementData(int index) {
return (E) elementData[index];//返回该数组中的索引位置的值
}