索引
1.介绍
2.常用参数
3.构造方法
4.增
5.删
6.改
8.查
9.总结
1.介绍
ArrayList是一个基于数组的集合实现,可以进行对元素的增删改查,可以动态的进行扩容。ArrayList并不是线程安全,所以效率比较高,推荐单线程环境下使用。
2.常用参数
- elementData :数组
- size:数组中元素的个数
- initialCapacity:作为参数传入,为集合的大小
3.构造方法
public ArrayList(int initialCapacity) {
//如果参数大于0,则创建一个initialCapacity大小的object数组
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
}
//如果参数等于0,则创建一个空数组
else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
}
否则抛出异常
else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
public ArrayList() {
//创建一个空数组
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//构造函数,传入一个容器
public ArrayList(Collection<? extends E> c) {
//对容器数组化
elementData = c.toArray();
//将容器中的元素传入数组
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
//创建一个空数组
this.elementData = EMPTY_ELEMENTDATA;
}
}
一共三个构造方法,一个传入数组大小来创建,一个创建一个空数组,最后一个传入一个容器来创建。
4.增
add方法的函数体比较简单,再此之前我们先来看看add方法内部调用的一些方法
- grow:这是一个扩容数组的方法
private void grow(int minCapacity) {
//传入数组长度为oldCapacity
int oldCapacity = elementData.length;
//定义一个新的长度newCapacity=原来数组的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
//如果新的长度小于参数传入的长度,则newCapacity等于传入参数
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//如果新的长度大于数组要求最大长度,则取最大长度
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//最后将数组引用指向复制的新数组
elementData = Arrays.copyOf(elementData, newCapacity);
}
- ensureExplicitCapacity:判断是否有必要扩容
private void ensureExplicitCapacity(int minCapacity) {
//检测add操作和remove操作的次数
modCount++;
// 传入参数大于数组长度,则扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
- ensureCapacityInternal:对于一个空数组,这个函数相当在第一次add时就将数组扩容为10。我认为这是一个非常巧妙的操作,在之前1.7版本,是在构造函数时,就创建了一个大小10的数组,这个则是在有add操作时才申请空间。
private void ensureCapacityInternal(int minCapacity) {
//判断如果这个数组等于空数组,则扩容为一个长度DEFAULT_CAPACITY=10(源码中默认为10)的数组
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
- add:添加元素函数
public boolean add(E e) {
//检查size+1是否大于数组长度,大于就扩容
ensureCapacityInternal(size + 1);
//添加元素
elementData[size++] = e;
return true;
}
5.删
代码及解释如下
- rangeCheck:对传入的索引进行边界检查
private void rangeCheck(int index) {
//太简单,不说了
if (index < 0 || index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
- remove
public E remove(int index) {
//越界检测
rangeCheck(index);
modCount++;
//将要删除的元素赋值
E oldValue = elementData(index);
//获取移动的位置的大小
int numMoved = size - index - 1;
if (numMoved > 0)
将数组的index+1位置开始向前移动到index位置,移动nummove个元素
System.arraycopy(elementData, index+1, elementData, index,numMoved);
//将最后的位置置为null,并size-1
elementData[--size] = null; // clear to let GC do its work
//返回删除的元素
return oldValue;
}
6.改
- set
public E set(int index, E element) {
//索引合法性检测
rangeCheck(index);
//老结果赋值给oldvalue
E oldValue = elementData(index);
//赋新值
elementData[index] = element;
//返回老的值
return oldValue;
}
7.查
- get
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
8.总结
ArrayList具有动态扩容的特点,每次扩容都是一次O(n)级别的操作。它的查询效率很高为O(1),但是其增删会伴随着一次O(n)级别数组赋值,这是它的缺点。