1简介
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
ArrayList使用一个可变数组实现List接口,实现了List接口的所有可选操作。ArrayList除了是非线程安全的之外,其他的与Vector类似。
2成员属性
//序列化版本号 private static final long serialVersionUID = 8683452581122892189L; //默认容量 private static final int DEFAULT_CAPACITY = 10; //默认数组,没有元素,空数组 private static final Object[] EMPTY_ELEMENTDATA = {}; //默认容量数组 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //存放元素的数组 transient Object[] elementData; //当前ArrayList里面元素存放数量 private int size;
3构造函数
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; } }
上面几个构造函数没什么好讲的,但有一个疑问,正常来说DEFAULTCAPACITY_EMPTY_ELEMENTDATA容量应该为10,可是我们在属性的里声明的时候却是为空的,为什么会这样?不急,接下来的扩容代码给出了很好的解释。与Vector一样,ArrayList可以调用trimToSize将多余的空间释放,也可以扩充容量。
4可收缩容量
(1)trimToSize方法
public void trimToSize() { modCount++; if (size < elementData.length) { elementData = (size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size); } }
size属性是集合里现有元素的个数,elementData.length是数组的实际长度。我们可以trimToSize将数组标号为[size,elementData.length-1]的空间释放。
(2)扩容
public void ensureCapacity(int minCapacity) { int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) // any size if not default element table ? 0 // larger than default for default empty table. It's already // supposed to be at default size. : DEFAULT_CAPACITY; if (minCapacity > minExpand) { ensureExplicitCapacity(minCapacity); } } private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, 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); 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; }
我们先解释一下DEFAULTCAPACITY_EMPTY_ELEMENTDATA容量问题:
当调用add方法添加元素时,首先会执行ensureCapacityInternal(size + 1),进入该方法后只有一条ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)),当elementData是DEFAULTCAPACITY_EMPTY_ELEMENTDATA时,calculateCapacity方法会返回DEFAULT_CAPACITY和size + 1的较大值。size默认从0开始的,所以第一次调用add方法时且当elementData是DEFAULTCAPACITY_EMPTY_ELEMENTDATA时,就会对进行扩容操作,而扩容的容量正好是DEFAULT_CAPACITY(10)。
然后我们分析手动调用ensureCapacity(minCapacity)进行扩容的过程:
进入该方法后,第一步是求minExpand,这里会判读实际数组是否等于DEFAULTCAPACITY_EMPTY_ELEMENTDATA,若是的话minExpand赋值为DEFAULT_CAPACITY(10),否则为0。然后确保参数minCapacity大于minExpand,才进行后续的扩容操作,调用ensureExplicitCapacity(minCapacity)。
ensureExplicitCapacity方法确保minCapacity大于当前容量elementData.length才进行扩容操作,调用 grow(minCapacity)。
因为扩容原理是复制数组,这是个是个很费计算机资源的操作,应该尽量减少扩容次数,所以每次扩容都会保证最小扩容(旧容量*1.5),grow方法的主要功能就是这个。
5查找
public boolean contains(Object o) { return indexOf(o) >= 0; } //查找第一个索引 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 int lastIndexOf(Object o) { if (o == null) { for (int i = size-1; i >= 0; i--) if (elementData[i]==null) return i; } else { for (int i = size-1; i >= 0; i--) if (o.equals(elementData[i])) return i; } return -1; } //获取索引位置元素 public E get(int index) { rangeCheck(index); return elementData(index); } //set放着一起看吧 public E set(int index, E element) { rangeCheck(index); E oldValue = elementData(index); elementData[index] = element; return oldValue; } private void rangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }
查找的时候注意判断null,因为null.equals()是会报空指针异常的。indexOf是从前往后遍历数组,lastIndexOf则是从后往前遍历。
6集合基本操作
(1)添加元素
//在末尾添加一个元素 public boolean add(E e) { ensureCapacityInternal(size + 1); // 检查是否需要扩容 elementData[size++] = e; return true; } //在指定位置添加一个元素 public void add(int index, E element) { rangeCheckForAdd(index); ensureCapacityInternal(size + 1); // Increments modCount!! System.arraycopy(elementData, index, elementData, index + 1,size - index); // index后的所有元素均后移一位 elementData[index] = element; size++; } //添加一堆元素 public boolean addAll(Collection<? extends E> c) { Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew); // Increments modCount System.arraycopy(a, 0, elementData, size, numNew); size += numNew; return numNew != 0; } //从指定位置开始添加一堆元素 public boolean addAll(int index, Collection<? extends E> c) { rangeCheckForAdd(index); Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(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; } private void rangeCheckForAdd(int index) { if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }
(2)删除元素
//删除指定位置的元素 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; } //删除匹配的元素 public boolean remove(Object o) { if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { fastRemove(index); return true; } } else { for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); return true; } } return false; } private void fastRemove(int index) { modCount++; 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 } //删除索引为[fromIndex,toIndex]范围内的所以元素 protected void removeRange(int fromIndex, int toIndex) { modCount++; int numMoved = size - toIndex; System.arraycopy(elementData, toIndex, elementData, fromIndex, numMoved); // clear to let GC do its work int newSize = size - (toIndex-fromIndex); for (int i = newSize; i < size; i++) { elementData[i] = null; } size = newSize; } //删除一堆元素 public boolean removeAll(Collection<?> c) { Objects.requireNonNull(c); //检查集合c不能为空,否则报空指针异常 return batchRemove(c, false); } private boolean batchRemove(Collection<?> c, boolean complement) { final Object[] elementData = this.elementData; int r = 0, w = 0; boolean modified = false; try { for (; r < size; r++) if (c.contains(elementData[r]) == complement) elementData[w++] = elementData[r]; } finally { // Preserve behavioral compatibility with AbstractCollection, // even if c.contains() throws. if (r != size) { System.arraycopy(elementData, r, elementData, w, size - r); w += size - r; } if (w != size) { // clear to let GC do its work for (int i = w; i < size; i++) elementData[i] = null; modCount += size - w; size = w; modified = true; } } return modified; } //清空(删除所有元素) public void clear() { modCount++; // clear to let GC do its work for (int i = 0; i < size; i++) elementData[i] = null; size = 0; }
remove和fastRemove的区别是后者没有范围检查,只有确定不会越界的情况下才会调用fastRemove来提升效率。
值得注意的是与Vector不同,这里删除一堆元素不是通过迭代器实现的,而是通过batchRemove方法,该批量删除方法会遍历数据数组。当complement参数为false时,把不属于参数集合里的元素替换到原数组里面。为true时,把原集合中属于参数集合里的元素替换到原数组里面。
7序列化方法
由于ArrayList实现了Serializable接口,我们知道它是可序列化的,而且该类自己实现了序列化writeObject和反序列化readObject方法,当ObjectOutputStream调用writeObject进行序列化时,会调用该类自己的writeObject。同理,ObjectInputStream也会调用该类自己的readObject方法实现反序列化。
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{ // Write out element count, and any hidden stuff int expectedModCount = modCount; s.defaultWriteObject(); // Write out size as capacity for behavioural compatibility with clone() s.writeInt(size); // Write out all elements in the proper order. for (int i=0; i<size; i++) { s.writeObject(elementData[i]); } if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } } private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { elementData = EMPTY_ELEMENTDATA; // Read in size, and any hidden stuff s.defaultReadObject(); // Read in capacity s.readInt(); // ignored if (size > 0) { // be like clone(), allocate array based upon size not capacity int capacity = calculateCapacity(elementData, size); SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity); ensureCapacityInternal(size); Object[] a = elementData; // Read in all elements in the proper order. for (int i=0; i<size; i++) { a[i] = s.readObject(); } } }
writeObject方法第一步是获得当前modCount,这样做的目的是确保在方法执行过程中,ArrayList对象没有其他线程修改。然后是调用ObjectOutputStream 的defaultWriteObject
方法将ArrayList对象的非static 和非transient的属性写出到当前流。下一步是将集合中当前数据量size写出,最后遍历数组,将每个位置的对象写出。
readObject方法的工作稍复杂,在进行反序列化之前需要2个工作,第一个是数据检查,确保读过来的是Object数组。第二个是开辟合理的空间存储取回来的数据。
8迭代器实现
ArrayList的祖先Collection继承了Iterable接口,ArrayList是通过内部类实现迭代器的。
public Iterator<E> iterator() { return new Itr(); } /** * An optimized version of AbstractList.Itr */ private class Itr implements Iterator<E> { int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; Itr() {} public boolean hasNext() { return cursor != size; } @SuppressWarnings("unchecked") public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; } public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } @Override @SuppressWarnings("unchecked") public void forEachRemaining(Consumer<? super E> consumer) { Objects.requireNonNull(consumer); final int size = ArrayList.this.size; int i = cursor; if (i >= size) { return; } final Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) { throw new ConcurrentModificationException(); } while (i != size && modCount == expectedModCount) { consumer.accept((E) elementData[i++]); } // update once at end of iteration to reduce heap write traffic cursor = i; lastRet = i - 1; checkForComodification(); } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
关于ArrayList迭代的实现请参考我的另一篇文章:Java迭代器
对于ArrayList不仅有其Iterator实现,还有ListIterator的实现。ListIterator不仅可以向后遍历,而且可以向前遍历。
private class ListItr extends Itr implements ListIterator<E> { ListItr(int index) { super(); cursor = index; } public boolean hasPrevious() { return cursor != 0; } public int nextIndex() { return cursor; } public int previousIndex() { return cursor - 1; } @SuppressWarnings("unchecked") public E previous() { checkForComodification(); int i = cursor - 1; if (i < 0) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i; return (E) elementData[lastRet = i]; } public void set(E e) { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.set(lastRet, e); } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } public void add(E e) { checkForComodification(); try { int i = cursor; ArrayList.this.add(i, e); cursor = i + 1; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } }
ListIterator向后迭代的过程继承自Iterator,这里没什么好说的。这里主要分析previous()、set(E e)、add(E e)三个方法。
(1)previous()方法
该方法定义前向迭代的过程,首先将当前游标cursor向前移动一位,再把游标移动后指向的位置赋值给lastRet,最后返回lastRet指向的值。
(2)set(E e)方法
该方法将lastRet指向位置的值改为指定值(参数)。
(3)add(E e)方法
该方法在cursor位置插入指定值,然后将游标位置后移1位,并将lastRet置为-1。
9其他
(1)排序
ArryList内部提供了排序方法sort,sort方法就是接受了一个Comparator比较器对象,然后将List集合转换成数组,然后再调用数组工具类Arrays的sort()方法进行排序,最后把排好序的数组遍历并赋值到原来的List集合上。
public void sort(Comparator<? super E> c) { final int expectedModCount = modCount; Arrays.sort((E[]) elementData, 0, size, c); if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } modCount++; }
(2)removeIf方法
该方法的调用依赖于函数编程,需要传入一个函数作为参数,符合该函数判定为true的元素进行删除操作。
1 public boolean removeIf(Predicate<? super E> filter) { 2 Objects.requireNonNull(filter); 3 // figure out which elements are to be removed 4 // any exception thrown from the filter predicate at this stage 5 // will leave the collection unmodified 6 int removeCount = 0; 7 final BitSet removeSet = new BitSet(size); 8 final int expectedModCount = modCount; 9 final int size = this.size; 10 for (int i=0; modCount == expectedModCount && i < size; i++) { 11 @SuppressWarnings("unchecked") 12 final E element = (E) elementData[i]; 13 if (filter.test(element)) { 14 removeSet.set(i); 15 removeCount++; 16 } 17 } 18 if (modCount != expectedModCount) { 19 throw new ConcurrentModificationException(); 20 } 21 22 // shift surviving elements left over the spaces left by removed elements 23 final boolean anyToRemove = removeCount > 0; 24 if (anyToRemove) { 25 final int newSize = size - removeCount; 26 for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) { 27 i = removeSet.nextClearBit(i); 28 elementData[j] = elementData[i]; 29 } 30 for (int k=newSize; k < size; k++) { 31 elementData[k] = null; // Let gc do its work 32 } 33 this.size = newSize; 34 if (modCount != expectedModCount) { 35 throw new ConcurrentModificationException(); 36 } 37 modCount++; 38 } 39 40 return anyToRemove; 41 }
line13的代码filter.test(element)判断符合条件的元素,这里通过位运算记录需要操作的位置,然后进行数组复制。(关于如何通过位运算记录删除位请详细阅读BitSet类源码)
(3)forEach方法
该方法和前面迭代器一节中的forEachRemaining类似,也是Java8函数编程新特性。forEach的作用是为集合中的每个元素执行一些操作(操作函数作为参数传入)
public void forEach(Consumer<? super E> action) { Objects.requireNonNull(action); final int expectedModCount = modCount; @SuppressWarnings("unchecked") final E[] elementData = (E[]) this.elementData; final int size = this.size; for (int i=0; modCount == expectedModCount && i < size; i++) { action.accept(elementData[i]); } if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } }
(4)subList
List<E> subList(int fromIndex, int toIndex);
- 该方法返回的是父list的一个视图,从fromIndex(包含),到toIndex(不包含)。fromIndex=toIndex 表示子list为空
- 父子list做的非结构性修改(non-structural changes)都会影响到彼此:所谓的“非结构性修改”,是指不涉及到list的大小改变的修改。相反,结构性修改,指改变了list大小的修改。
- 对于结构性修改,子list的所有操作都会反映到父list上。但父list的修改将会导致返回的子list失效。
- 如何删除list中的某段数据:list.subList(from, to).clear();
代码这里我就不贴了。