List接口的继承关系图
ArrayList
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
}
分析ArrayList的类定义,可以得到其实现了List接口, 以及继承了AbstractList类, 而AbstractList类又是 AbstractCollection的子类,AbstractCollection又实现了Collection接口 !
- AbstractList 抽象类
- Collection 接口
- AbstractCollection 抽象类
- List 接口
这就意味着 AbstractCollection 抽象类会实现 Collection 接口中的一部分方法!而它的子类AbstractList 又会重写一部分 AbstractCollection 实现了函数! 等到了ArrayList 这一层次, 对于一些集合的通用方法,它便可以直接使用AbstractList 中实现的方法,而不用全部实现List接口中的所有方法!
构造函数
new ArrayList<>()
构造函数, 构造出来一个空的集合,并且使用给定的大小初始化。可以得到以下信息
- 可以指定大小创建ArrayList
- 其底层是由数组实现
- 具有默认的初始化大小
/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
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);
}
}
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
添加方法
add
添加方法
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
ensureCapacityInternal
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
calculateCapacity
此方法可以判断当前集合是否第一次添加元素!第一次添加元素的话, 就会把当前需要添加元素的位置与默认的10比较, 返回其最大的!非首次添加元素的话, 就会直接返回当前值!得到的信息如下
-
ensureCapacityInternal(size + 1); 当前元素添加的位置
-
DEFAULT_CAPACITY = 10
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
ensureExplicitCapacity
上述方法的返回值将作为此函数的入参! 并且判断是否需要扩容 !
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
不扩容情况
此时回到 add方法中,我们执行到了 ensureCapacityInternal(size + 1); 然后把要添加的元素放到数组中可以了!
- elementData 当前集合中的数组
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
扩容的情况
回到 ensureExplicitCapacity 方法中
grow(minCapacity)
增加容量,以确保至少可以容纳最小容量参数指定的元素数。
- MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
private void grow(int minCapacity) {
// 得到当前数组的长度
int oldCapacity = elementData.length;
// 扩容 1.5 倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 扩容1.5 倍之后还不能放下当前元素, 就直接扩容到当前位置!
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果扩容之后的大小, 比Integer.MAX_VALUE - 8;还要大! 就直接扩容到整形的最大值!
// 否则就扩容到 Integer.MAX_VALUE - 8
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);
}
hugeCapacity
会判断当前元素要存放的位置是否大于, Integer.MAX_VALUE - 8 如果比它大, 就直接扩容到整形的最大值, 否则就扩容到Integer.MAX_VALUE - 8 。
- minCapacity 当前元素要放到数组中的位置
- MAX_ARRAY_SIZE : Integer.MAX_VALUE - 8;
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
elementData = Arrays.copyOf(elementData, newCapacity);
确定扩容的大小之后, 就开始使用 Arrays 直接复制。省略中间的过程,就是新建了个数组, 然后复制元素,最后把新的数组赋值给之前的elementData(数组)
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;
}
指定位置添加
add(int index, E element)
关键信息
- System.arraycopy(elementData, index, elementData, index + 1,size - index);
public void add(int index, E element) {
// 检查范围
rangeCheckForAdd(index);
// 检查可扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
// index位置后面的元素全部往后复制一个位置
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
// 添加当前位置的元素
elementData[index] = element;
// 修改数组大小
size++;
}
删除方法
首先会检查当前操作的位置在不在范围之内, 其次 改变modCount的值。得到需要移动元素的个数,将 index + 1以及后面的元素向前移动一位, 最后在把数组最后一个位置置为空!
主要方法
- System.arraycopy(elementData, index+1, elementData, index, numMoved);
public E remove(int index) {
// 检查当前操作的位置在不在范围之内
rangeCheck(index);
// 更新修改次数
modCount++;
E oldValue = elementData(index);
// 得到需要移动元素的个数
int numMoved = size - index - 1;
if (numMoved > 0)
// 将 index + 1以及后面的元素向前移动一位
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 最后在把数组最后一个位置置为空!
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
GET方法
- 先检查范围
- 直接返回当前位置的元素
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
SET方法
- 检查范围
- 直接覆盖当前位置的元素
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
找到元素的索引
如果当前元素是一个空值的话, 就直接在数组中找到为空的元素,直接返回。非空值的话, 就会使用equals 方法来判断对象是否相同!
- 使用equals 方法来判断对象是否相同!
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 class demo {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
System.out.println(list.size());
for (int i = 0; i < 10; i++) {
list.add(i);
}
System.out.println(list.size() + "- >" + getCapacity(list));
list.add(12);
list.add(13);
System.out.println(list.size() + "- >" + getCapacity(list));
list.add(Integer.MAX_VALUE - 9, 444);
System.out.println(list.size() + "- >" + getCapacity(list));
}
/**
* 使用反射来获得当前集合中的容量
*
* @param list
* @return
*/
public static Integer getCapacity(ArrayList list) {
Integer length = null;
Class c = list.getClass();
Field f;
try {
f = c.getDeclaredField("elementData");
f.setAccessible(true);
Object[] o = (Object[]) f.get(list);
length = o.length;
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return length;
}
}
内部常用函数
rangeCheck
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
arraycopy
调用本地方法实现数组的复制操作,参数信息如下:
- @param src 当前的资源数组
- @param srcPos 资源数组中开始位置
- @param dest 资源数组的目的位置
- @param destPos 目的资源(数组)开始的位置
* @param src the source array. // 当前的资源数组
* @param srcPos starting position in the source array.// 资源数组中开始位置
* @param dest the destination array.// 资源数组的目的位置
* @param destPos starting position in the destination data.// 目的资源(数组)开始的位置
* @param length the number of array elements to be copied.// 将要被复制元素的数量
*/
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
小总结
- arraylist 默认的容量大小是 10, 即使里面一个元素都没有。
- 当要数组中的元素比容量大的时候, 会选择先扩容后插入元素。
- 每次扩容都会在原来的基础之上增加 0.5 倍。
- 扩容时候会新建立一个数组, 然后把元素复制过去。