前文链接List接口详解
今天把明天的份也写了如何?
ArrayList是JAVA开发者最常用的列表没有之一啦,这个类通过继承虚基类AbstractList的方式实现了List接口,顺便也实现了Collection接口及Lterable接口,以上三个接口的用处前文均已说过,这里不废话。
同时ArrayList类还实现了RandomAccess(标记接口,用于队列,实现该接口表示支持快速随机访问)、Cloneable(同样是标记接口,实现该接口表示重写了Object.clone()方法,可以合法的通过clone()方法复制)、serializable(标记接口,实现该接口使此类可序列化,否则会在序列化时保存,配合serialVersionUID可以标识类版本)
ArrayList,顾名思义是数组列表的意思,作为List接口的可调大小数组实现,这个类实现了所有可选的列表操作,允许包括null在内的所有数组元素,提供了调整内部数组大小的方法。能够以O(n)速度实现绝大多数操作,值得注意的是ArrayList是不同步的,而隔壁的Vector则是同步的。可以通过List list = Collections.synchronizedList(new ArrayList(...));语句来加锁。
值得注意的是,ArrayList的迭代器是“快速失败“的,快速失败的详解可以参考快速失败,过几天我自己也会写一篇迭代器的解析,因为Literator(迭代器)在被创建之后,任何不使用该迭代器的remove()或者add()改变ArrayList结构的方法都会导致该错误,这个特性同时也导致了我们不能在普通的for循环中调用list的remove()方法删除元素。jdk1.8后,这种情况我们可以使用removeAll()方法更优雅的解决。
接下来是ArrayList一些比较有趣的常量和方法介绍:
常量:
1.serialVersionUID,没什么好说的,用于序列化时标明类版本
2 DEFAULT_CAPACITY=10,默认的初始化容量,大小为10个元素
3 EMPTY_ELEMENTDATA,空元素组,初始化时使用
4.DEFAULTCAPACITY_EMPTY_ELEMENTDATA 默认容量空元素数组,初始化时使用
5.elementData 元素数组,ArrayList数组的数据保存在这个数组中,放入第一个元素时如果是空数组也就是等于上面的EMPTY_ELEMENTDATA,会被给与一个初始大小(capacity)
6.size,emmm不废话
方法:
1.构造函数ArrayList(),有三种入参,输入int的话会按照输入的大小初始化一个数组,不输入或者输入0会让elementData =DEFAULTCAPACITY_EMPTY_ELEMENTDATA,也就是空数组啦,输入Collection(集合的话)会调用Collection.toArray()方法转成数组,如果转失败(结果不是Object[])的话就没下文,成功的话就调用Arrays.copyOf方法复制这个数组到elementData数组。
2.trimToSize()方法。将elementData 的大小调整到size大小,用来节约空间,值得注意的是这里用的还是Arrays.copyOf方法,下一篇就写Arrays吧。同样值得注意的是调整大小之前这个方法还将modCount(ArrayList未声明,直接继承于父类AbstractList)值+1了,这个操作的解释见modCount
3.ensureCapacity(int size)、ensureCapacityInternal()、ensureExplicitCapacity()方法,这一套素质三连可以添加elementDate的大小,这里还做了一个复杂判断让小数组的大小尽量为默认值(10),反正最后会调用grow()方法扩大大小
4.grow()方法,这里要提到一个新常量MAX_ARRAY_SIZE,大小为INTEGER.MAX_SIZE-8,这个常量是ArrayList大小可以达到的最大值,某些虚拟机要占用一下头字节还会再少一点,反正超过了就报OOM错误啦。
回到代码里,这个方法还用了位运算,挺有趣的贴一下
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
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);
}private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
反正右移一位是除2,就是说会扩容1.5倍,如果还是不够大就是按照输入的大小来,如果大小溢出的话就调用hugeCapacity,反正数组大小不会超过Integer的最大值啦
5.size(),isempty(),方法就是用了一下size这个变量
6.index(),lastindexof(),这两个方法分别是从数组的前后方做遍历,不提
7.clone()方法,先调用super.clone(),然后用Arrays.copyOf()方法(怎么又是你)复制关键的数组,然后把modCount置空
8.toArray()方法,无参数的话还是Arrays.copyOf(),有参数的话就比较有意思,如下:
@SuppressWarnings("unchecked")
public T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
这里调用的是System.arraycopy()方法,是一个native方法,emmmm等我看Arrays的时候再看这个的解释,然后还判断了一下数组的长度,如果超过的话就将a[size]置空,这个的意义何在?不太理解
9.elementDate()方法,project方法,返回数组指定位置元素,未验证安全
10.get()方法,验证index范围后调用elementDate()方法
11.set(),验证范围后直接操作数组