ArrayList这个常用的类,相信都经常用,但是它具体实现关注过吗?这次就来看看它具体怎么实现的吧。
首先看下它的继承关系
![03ed4f3156fd725d2db619d744756516.png](https://img-blog.csdnimg.cn/img_convert/03ed4f3156fd725d2db619d744756516.png)
ArrayList继承关系
可以看到它继承了AbstractList,并是List等接口的实现类。
那么我们看下源码翻译吧
List接口的可调整数组实现。实现所有可选的列表操作,并允许放入所有元素,包括null。除了实现List接口之外,ArrayList还提供了一些方法来操作内部用于存储列表的数组。(这个类大致相当于Vector,只是它unsynchronized。)
size、isEmpty、get、set、iterator、listIterator这些操作以恒定时间执行,add操作是也就是摊余常数时间,就是说,添加n个元素需要O(n)时间。所有其他操作都是在线性时间内运行的(粗略地说)。与LinkedList实现相比,常数因子较低。
每个ArrayList实例都有一个容量。容量是用于存储列表中元素的数组的大小。它至少和列表大小一样大。当元素被添加到ArrayList时,它的容量会自动增长。除了增加一个元素具有固定的摊余时间成本外,增长策略的时间成本没有被计算在内。
在使用ensureCapacity操作添加大量元素之前,应用程序可以手动增加ArrayList实例的容量。这可能会减少重新分配容器大小的次数。
注意ArrayList的实现不是线程安全的。如果多个线程同时访问ArrayList实例,并且至少有一个线程在结构上修改了该列表,则必须在外部保证线程安全性。(结构修改是指添加或删除一个或多个元素,或显式调整数组大小的任何操作;仅仅设置元素的值不是结构修改。)这通常是通过在封装列表的某个对象上synchronize来完成的。
如果不存在这样的对象,则应该使用Collections.synchronizedList方法,最好在创建时执行此操作,以防止意外的非安全线程访问列表。
List list = Collections.synchronizedList(new ArrayList(...));
如果列表在迭代器创建后的任何时候进行了结构上的修改,除了iterator自身的remove和add方法,其他的方法都会执行fail-fast操作,抛ConcurrentModificationException异常。因此,在并发修改的情况下,迭代器会迅速失败操作,而不是在将来某个不确定的时间冒着不确定的行为的风险操作。
注意:迭代器的fail-fast行为不能得到安全性保证,因为一般来说,在存在不安全的并发修改时,不可能做出任何绝对保证。Fail fast迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写一个依赖于这个异常来保证其正确性的程序是错误的,迭代器的快速失效行为应该只用于检测错误。
这个类是JAVA的Collections框架。
源码翻译完毕,可以看到并没有怎么说具体使用方法,因为比较常见,主要是围绕着线程安全操作去介绍的。那么下来看看源代码吧。
首先看下属性和构造方法。
/** * Default initial capacity. */ private static final int DEFAULT_CAPACITY = 10; /** * Shared empty array instance used for empty instances. */ private static final Object[] EMPTY_ELEMENTDATA = {}; /** * Shared empty array instance used for default sized empty instances. We * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when * first element is added. */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. Any * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA * will be expanded to DEFAULT_CAPACITY when the first element is added. */ transient Object[] elementData; // non-private to simplify nested class access /** * The size of the ArrayList (the number of elements it contains). private int size;public ArrayList(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } 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 { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; }}
可以看到,其实就是用一个Object去存储对象,用size表示数组当前数量大小。
那么接下来就看看增删改查吧。
先看看增
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } 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 static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1);//增加1.5倍 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity);//最大分配Integer.MAX_VALU // 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; }
可以看到add->ensureCapacityInternal(如果一开始elementData是空,minCapacity是初始化10,其他就是size + 1)->ensureExplicitCapacity(是操作数量modCount加一,如果现在elementData已经到达目前elementData分配大小最多就执行)->grow(就是分配1.5倍空间,如果到达MAX_ARRAY_SIZE值,最大分配Integer.MAX_VALUE)->最后elementData[size++] = e;
再看看删除
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; } private void rangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } E elementData(int index) { return (E) elementData[index]; }
可以看到就是先检查index是否超过size,然后modCount加一,之后获取对应index的elementData数据,然后通过System.arraycopy补充删除位置,之后将最后位置设置空,范围删除位置内容。
再看看改,更简单。
public E set(int index, E element) { rangeCheck(index); E oldValue = elementData(index); elementData[index] = element; return oldValue; }
执行要将index换成对应新对象即可。
最后就是查了,来看看吧
public int indexOf(Object o) { if (o == null) { for (int i = 0; i < size; i++) if (elementData[i]==null) return i; } else { for (int i = 0; i < size; i++) if (o.equals(elementData[i])) return i; } return -1;}public E get(int index) { rangeCheck(index); return elementData(index);}
可以看到常用的indexOf很简单就是for循环去通过equals查到对应位置的index。get更简单就是获取index的位置的elementData的数据。
OK,今天分析完了。