Java集合--ArrayList源码
ArrayList是Java的动态数组集合,主要用于快速读取数组元素,且在读多写少的情况下具有较好的表现;相比于LinedList,更节省空间,因为LinkedList的元素还要多存储前后继节点的指针,相较于ArrayList只存储元素本身有一定的差距,但是ArrayList在使用不当的时候也容易浪费内存,例如大量数据不断写入ArrayList时,会不断分配新的内存,并复制原有数据到新的内存中。
1 /** 2 * The array buffer into which the elements of the ArrayList are stored. 3 * The capacity of the ArrayList is the length of this array buffer. Any 4 * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 5 * will be expanded to DEFAULT_CAPACITY when the first element is added. 6 */ 7 transient Object[] elementData; // non-private to simplify nested class access
elementData数组是ArrayList最重要的属性之一,用于存储实际的数据;elementData为什么会被定义为transient关键字修饰呢?因为ArrayList多数情况下会出现空余数组空间,如果序列化全部的数组,那么会造成时间和空间的额外消耗;ArrayList提供了自己的writeObject方法和readObject方法,只序列化了实际存储的元素。
/** * The size of the ArrayList (the number of elements it contains). * * @serial */ private int size;
size是elementData数组中实际存储的元素的个数,区别于elementData.length,在ArrayList中,elementData数组的长度是ArrayList的实际开辟空间的长度
/** * This helper method split out from add(E) to keep method * bytecode size under 35 (the -XX:MaxInlineSize default value), * which helps when add(E) is called in a C1-compiled loop. */ private void add(E e, Object[] elementData, int s) { if (s == elementData.length) elementData = grow(); //grow()方法扩充开辟新空间,并复制数据到新空间 elementData[s] = e; size = s + 1; } /** * Appends the specified element to the end of this list. * * @param e element to be appended to this list * @return {@code true} (as specified by {@link Collection#add}) */ public boolean add(E e) { modCount++; add(e, elementData, size); return true; }
从add(E e)方法中可以分析出,数组中当前元素的个数等于数组的长度时,会增长当前数组的长度;
/** * Increases the capacity to ensure that it can hold at least the * number of elements specified by the minimum capacity argument. * * @param minCapacity the desired minimum capacity * @throws OutOfMemoryError if minCapacity is less than zero */ private Object[] grow(int minCapacity) { return elementData = Arrays.copyOf(elementData, newCapacity(minCapacity)); } private Object[] grow() { return grow(size + 1); } /** * Returns a capacity at least as large as the given minimum capacity. * Returns the current capacity increased by 50% if that suffices. * Will not return a capacity greater than MAX_ARRAY_SIZE unless * the given minimum capacity is greater than MAX_ARRAY_SIZE. * * @param minCapacity the desired minimum capacity * @throws OutOfMemoryError if minCapacity is less than zero */ private int newCapacity(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity <= 0) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) return Math.max(DEFAULT_CAPACITY, minCapacity); if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return minCapacity; } return (newCapacity - MAX_ARRAY_SIZE <= 0) ? newCapacity : hugeCapacity(minCapacity); }
扩充数组容量时,会成倍增长,如果数据量较大又未开辟足够的空间,且不断写入新数据的时候,ArrayList需要不断开辟新空间又要复制数据,性能较差且可能浪费内存空间;
在add(),set(),remove()等写方法中,会看到modCount得增加,这个字段有什么作用呢?在AbstractList中能看到modCount的定义
/** * The number of times this list has been <i>structurally modified</i>. * Structural modifications are those that change the size of the * list, or otherwise perturb it in such a fashion that iterations in * progress may yield incorrect results. * * <p>This field is used by the iterator and list iterator implementation * returned by the {@code iterator} and {@code listIterator} methods. * If the value of this field changes unexpectedly, the iterator (or list * iterator) will throw a {@code ConcurrentModificationException} in * response to the {@code next}, {@code remove}, {@code previous}, * {@code set} or {@code add} operations. This provides * <i>fail-fast</i> behavior, rather than non-deterministic behavior in * the face of concurrent modification during iteration. * * <p><b>Use of this field by subclasses is optional.</b> If a subclass * wishes to provide fail-fast iterators (and list iterators), then it * merely has to increment this field in its {@code add(int, E)} and * {@code remove(int)} methods (and any other methods that it overrides * that result in structural modifications to the list). A single call to * {@code add(int, E)} or {@code remove(int)} must add no more than * one to this field, or the iterators (and list iterators) will throw * bogus {@code ConcurrentModificationExceptions}. If an implementation * does not wish to provide fail-fast iterators, this field may be * ignored. */ protected transient int modCount = 0;
modCount用来记录链表被修改的次数;典型的场景是:当迭代器对链表操作时,其他方法修改了该链表,那么通过对比modCount的值就可以判断链表在使用的时候是否被修改过,如果被修改过,立即抛出ConcurrentModificationException异常。这就是AbstractList的fail-fast机制,而不是出现不确定的行为。
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
checkForComodification()方法定义在ArrayList的私有内部类中,迭代器常用的next方法和remove方法都会调用该方法,用于快速检测链表是否被其他行为修改
CopyOnWriteArrayList是一个写时拷贝的动态数组,是线程安全的;
/** The lock protecting all mutators */ final transient ReentrantLock lock = new ReentrantLock();
/** * Appends the specified element to the end of this list. * * @param e element to be appended to this list * @return {@code true} (as specified by {@link Collection#add}) */ public boolean add(E e) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len + 1); newElements[len] = e; setArray(newElements); return true; } finally { lock.unlock(); } }
在进行add操作的时候,首先获取重入锁,更新数组的副本后在将副本赋值给array。读操作不会加锁