一、概述
底层存储结构:数组
当数组存储的容量达到上限,则需要进行扩容,ArrayList扩容的规则是超出限制时增加当前容量的50%,即
newCapacity = oldCapacity + (oldCapacity >> 1);ArrayList默认初始容量为10;
数组的优势在于查找和修改某值的效率高,因而ArrayList按数组下标访问第i个元素(get(i))或修改第i个元素(set(i, o))的效率高。
在数组末尾插入元素add(o)的效率也高;
但是基于下标插入(add(i, o))或删除某个元素(remove(i),remove(o)此处若ArrayList中对象为Integer类型,若为int,则移除对应的下标,若为Integer,则移除元素)
对于中间插入和删除的情况,则会使用System.arraycopy()来移动部分受影响的元素,性能就有所下降,则也是数组的数据结构的缺点。
二、add()方法
该方法的底层源码如下:
public boolean add(E e){
ensureCapacityInternal(size + 1);
elementData[size ++] = e;
return true;
}ensureCapacityInternal()方法是自动扩容机制的核心,先附上源码:
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// 内存扩张,即oldCapacity + oldCapacity/2
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果扩为1.5倍还不满足需求,直接扩为需求值
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
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);
}通过让目前容量加1,与当前的最大容量比较,若超过则进行扩容,否则直接添加到集合中即可。
扩容的规则是目前容量加上目前容量的一半,为扩容后容量,即:
newCapacity = oldCapacity + (oldCapacity >> 1);底层通过Arrays.copyOf()进行扩容。
对于add(int index, E element)方法,先附上其源码:
public void add(int index, E element){
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); //Increments modCount!!!
System.arraycopy(elementData, index, elementData, index + 1, size - index);
elementData[index] = element;
size ++;
}该方法是先将数组从index + 1起的元素使用System.arraycopy()方法向后移动移位。
注意这儿使用数组复制方法是System.arraycopy()方法。文章最后补充这两种方法的区别。
三、set和get方法
Array的set和get方法比较简单,先检测index是否越界,不越界则执行赋值或访问。
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;
}注意:set()方法返回值,是返回旧值,这儿需注意下。
四、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;
}在该方法中注意将数组长度size最后一个置位null,释放内存空间。
补充:数组拷贝主要的四种方法:
分别是循环赋值、System.arraycopy()、Arrays.copyOf()(或者Arrays.copyOfRange)和close()方法。
- 循环拷贝(速度相对比较慢),即for循环,可以进行深拷贝或者浅拷贝
- System.arraycopy()(浅拷贝),这是系统提供的拷贝方法,也是我们推荐使用的拷贝方式,它是浅拷贝!!!
底层是通过C或者C++实现的,即native修饰的方法,因而速度比较快。
public static native void arraycopy(Object src, int sroPos, Object dest, int destPos, int length);- Arrays.copyOf()(浅拷贝)
底层是通过调用System.arraycopy()实现的
public static byte[] copyOfRange(byte[] original, int from, int to) {
int newLength = to - from;
if (newLength < 0)
throw new IllegalArgumentException(from + " > " + to);
byte[] copy = new byte[newLength];
System.arraycopy(original, from, copy, 0,
Math.min(original.length - from, newLength));
return copy;
}
}- Object.close(),close()比较特殊,对于对象而已,它是深拷贝,但是对于数组而言,它是浅拷贝。