从事开发工作都3个年头了,对应用频率最高的ArrayList容器老早之前想进行看看底层是怎么实现的, 由于各种原因一直被挡搁,到现在还没有一睹芳容。最近有点时间顺道看看java底层api实现方式
针对ArrayList容器本身存放的是对象数组元素和对应数组元素的长度
源码:
private transient Object[] elementData;
private int size;
整个ArrayList容器说白了是针对elementData数组进行操作的
既然看ArrayList容器那就重那几个最基本常用的方法进行看起
1、创建默认构造
public ArrayList() {
this(10);
}
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
从这两个构造方法可以看出对应ArrayList的对象进行创建时,就开始对数组分配大小,如果选择默认构造那就是默认设定数组的大小是10,也可以进行传参数进行设定数组的初始大小
2、既然创建好对象了下一步就开始进行放入ArrayList中值,add 方法
public boolean add(E e) {
ensureCapacity(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
先从最简单的add(E e)方法进行看起。里面有一个方法ensureCapacity(size+1)方法,先看方法源码
public void ensureCapacity(int minCapacity) {
modCount++;
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
Object oldData[] = elementData;
int newCapacity = (oldCapacity * 3)/2 + 1;
if (newCapacity < minCapacity)
newCapacity = minCapacity;
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
从这个方法源码可以看出,是给数组扩大存储空间的,当需要添加一个元素,如果数组的空间足够,那直接不进行操作
如果出现数组空间不够的话就会按照oldCapacity*3/2+1的方式进行扩容,举个例子进行说明一下:
如果ArrayList按照默认的进行创建一个对象并且里面已经放入了10个对象,现在通过add方法进行再次添加一个对象的话则ArrayList容器中的数组大小则变成10*3/2+1=16 即增长大小为原大小1/2+1的长度。
把两个方法都看完似乎明白了,原来ArrayList的add方法就是先进行判断一下是不是其中数组空间是不是不足了,如果不足的话就按照规则进行扩容;现在有了足够的大小,随后就把传来的对象赋值放到数组的最后边
既然普通的add(E e)方法通过这种方式进行存放对象,那对应有时候用到的add(int index,E e)这个方法又是如何实现的呢?
现在看对应源码
public void add(int index, E element) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(
"Index: "+index+", Size: "+size);
ensureCapacity(size+1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
看到这里似乎明白了
ensureCapacity(size+1)这个方法是扩容的
System.arrayCopy()这个方法是调用底层C语言写的方法进行把老数组中的数据按照制定方式重新拷贝到数组中,然后进行再指定数组位置给放入对象。例如
ll.add(11);
ll.add(1,22);
对应ArrayList中的数组存数据的为[22,11]这个方式
那普通的add(E e)方法是如此进行操作的那对应addAll(Collection c)又是如何进行工作的呢?
源码:
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacity(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
哦,看到源码就可以全部明白了。原来底层是进行先把对应Collection对象转换成对应数组然后把新老数组进行合并,长度就是对应之前两个数组长度之和。
注:看这段代码显得挺正确,其实不然,其中存在着一个bug,针对Object[] a = c.toArray();返回的对象有可能不是Object[]类型的。需要进行转换一下才能进行System.arraycopy操作。在jdk1.7中已经修复。请查看jdk1.7源码
最后就剩下一个addAll(int index,Collection c)
源码:
public boolean addAll(int index, Collection<? extends E> c) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(
"Index: " + index + ", Size: " + size);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacity(size + numNew); // Increments modCount
int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
这个方法就不进行多解释了,请参照add(int index,E e)与方法add(Collection c)两个方法