ArrayList的主要特点
添加元素
有这么一段代码:
public static void main(String[] args)
{
List<String> list = new ArrayList<String>();
list.add("000");
list.add("111");
}
看下底层会做什么,进入add方法的源码来看一下:1 public boolean add(E e) {
2 ensureCapacity(size + 1); // Increments modCount!!
3 elementData[size++] = e;
4 return true;
5 }
先不去管第2行的ensureCapacity方法,这个方法是扩容用的,底层实际上在调用add方法的时候只是给elementData的某个位置添加了一个数据而已,用一张图表示的话是这样的:多说一句,我这么画图有一定的误导性。elementData中存储的应该是堆内存中元素的引用,而不是实际的元素,这么画给人一种感觉就是说elementData数组里面存放的就是实际的元素,这是不太严谨的。不过这么画主要是为了方便起见,只要知道这个问题就好了。
扩容
我们看一下,构造ArrayList的时候,默认的底层数组大小是10: (有的地方看到的写的是16)
public ArrayList() {
this(10);
}
那么有一个问题来了,底层数组的大小不够了怎么办?答案就是扩容,这也就是为什么一直说ArrayList的底层是基于动态数组实现的原因,动态数组的意思就是指底层的数组大小并不是固定的,而是根据添加的元素大小进行一个判断,不够的话就动态扩容,扩容的代码就在ensureCapacity里面:public void ensureCapacity(int minCapacity) {
<span> </span> modCount++;
<span> </span>int oldCapacity = elementData.length;
<span> </span>if (minCapacity > oldCapacity) {
<span> </span>Object oldData[] = elementData;
<span> </span>int newCapacity = (oldCapacity * 3)/2 + 1;
<span> </span>if (newCapacity < minCapacity)
<span> </span>newCapacity = minCapacity;
<span> </span>// minCapacity is usually close to size, so this is a win:
<span> </span>elementData = Arrays.copyOf(elementData, newCapacity);
<span> </span>}
}
看到扩容的时候把元素组大小先乘以3,再除以2,最后加1。可能有些人要问为什么?我们可以想:
1、如果一次性扩容扩得太大,必然造成内存空间的浪费
2、如果一次性扩容扩得不够,那么下一次扩容的操作必然比较快地会到来,这会降低程序运行效率,要知道扩容还是比价耗费性能的一个操作
所以扩容扩多少,是JDK开发人员在时间、空间上做的一个权衡,提供出来的一个比较合理的数值。最后调用到的是Arrays的copyOf方法,将元素组里面的内容复制到新的数组里面去:
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
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;
}
用一张图来表示就是这样的:
删除元素
接着我们看一下删除的操作。ArrayList支持两种删除方式:
1、按照下标删除
2、按照元素删除,这会删除ArrayList中与指定要删除的元素匹配的第一个元素
对于ArrayList来说,这两种删除的方法差不多,都是调用的下面一段代码:
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // Let gc do its work
其实做的事情就是两件:
1、把指定元素后面位置的所有元素,利用System.arraycopy方法整体向前移动一个位置
2、最后一个位置的元素指定为null,这样让gc可以去回收它
比方说有这么一段代码:
public static void main(String[] args)
{
List<String> list = new ArrayList<String>();
list.add("111");
list.add("222");
list.add("333");
list.add("444");
list.add("555");
list.add("666");
list.add("777");
list.add("888");
list.remove("333");
}