jdk version:1.8

成员变量

  1. public class ArrayList<E> extends AbstractList<E>
  2. implements List<E>, RandomAccess, Cloneable, java.io.Serializable
  3. {
  4. private static final long serialVersionUID = 8683452581122892189L;
  5. /**
  6. * 默认的容量
  7. */
  8. private static final int DEFAULT_CAPACITY = 10;
  9. /**
  10. * 初始化 ArrayList 的时候,指定的容量为 0 时,指定的默认空容器
  11. */
  12. private static final Object[] EMPTY_ELEMENTDATA = {};
  13. /**
  14. * 通过无参构造创建 ArrayList 时,指定的默认空容器,和EMPTY_ELEMENTDATA 区分开来是用来标记当添加第一个
  15. * 元素的时候,应该膨胀的容量是多少,最少 DEFAULT_CAPACITY 个,详情可见 calculateCapacity 方法。
  16. */
  17. private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
  18. /**
  19. * 缓存元素的数组
  20. */
  21. transient Object[] elementData; // non-private to simplify nested class access
  22. /**
  23. * 记录元素个数
  24. * @serial
  25. */
  26. private int size;
  27. }

关键字 transient 是表示序列化的时候这个字段不被序列化 transient

构造方法

  1. public ArrayList(int initialCapacity) {
  2. if (initialCapacity > 0) {
  3. //直接创建一个新数组
  4. this.elementData = new Object[initialCapacity];
  5. } else if (initialCapacity == 0) {
  6. //指定初始化容器数量是 0
  7. this.elementData = EMPTY_ELEMENTDATA;
  8. } else {
  9. throw new IllegalArgumentException("Illegal Capacity: "+
  10. initialCapacity);
  11. }
  12. }
  13. /**
  14. * 无参构造,指定 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 为默认的空数组
  15. */
  16. public ArrayList() {
  17. this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
  18. }
  19. public ArrayList(Collection<? extends E> c) {
  20. elementData = c.toArray();
  21. if ((size = elementData.length) != 0) {
  22. // c.toArray might (incorrectly) not return Object[] (see 6260652)
  23. if (elementData.getClass() != Object[].class)
  24. elementData = Arrays.copyOf(elementData, size, Object[].class);
  25. } else {
  26. // 原数组的数量为 0,也是默认等于 EMPTY_ELEMENTDATA
  27. this.elementData = EMPTY_ELEMENTDATA;
  28. }
  29. }

增、改

add、set、adllAll

  1. public E set(int index, E element) {
  2. //检测 index 是否不比 size 小,判断条件: if (index >= size)
  3. //如果是则抛出 IndexOutOfBoundsException
  4. rangeCheck(index);
  5. //将新值覆盖到旧值上,并将原来的值返回出去
  6. E oldValue = elementData(index);
  7. elementData[index] = element;
  8. return oldValue;
  9. }
  10. public boolean add(E e) {
  11. //扩容
  12. ensureCapacityInternal(size + 1); // Increments modCount!!
  13. //在末尾添加元素,并且 size+1
  14. elementData[size++] = e;
  15. return true;
  16. }
  17. public void add(int index, E element) {
  18. //检测 index 是否不符合条件,判断条件: if (index > size || index < 0)
  19. //如果不符合则抛出 IndexOutOfBoundsException
  20. rangeCheckForAdd(index);
  21. //扩容
  22. ensureCapacityInternal(size + 1); // Increments modCount!!
  23. //将index 以及右边的元素往后移动一位
  24. System.arraycopy(elementData, index, elementData, index + 1,
  25. size - index);
  26. //为 index 下标附上新的值
  27. elementData[index] = element;
  28. size++;
  29. }
  30. public boolean addAll(Collection<? extends E> c) {
  31. Object[] a = c.toArray();
  32. int numNew = a.length;
  33. //扩容
  34. ensureCapacityInternal(size + numNew); // Increments modCount
  35. //直接从 size 下标开始进行复制赋值
  36. System.arraycopy(a, 0, elementData, size, numNew);
  37. size += numNew;
  38. return numNew != 0;
  39. }
  40. public boolean addAll(int index, Collection<? extends E> c) {
  41. //检测 index
  42. rangeCheckForAdd(index);
  43. Object[] a = c.toArray();
  44. int numNew = a.length;
  45. //扩容
  46. ensureCapacityInternal(size + numNew); // Increments modCount
  47. int numMoved = size - index;
  48. if (numMoved > 0)
  49. //numMoved >0 说明要移动原来的数据
  50. //在这里,由于 rangeCheckForAdd 检测了 index,所以 numMoved 只可能会>0 或者 ==0.
  51. //==0时,该方法和 addAll(Collection<? extends E> c) 没什么区别。
  52. System.arraycopy(elementData, index, elementData, index + numNew,
  53. numMoved);
  54. System.arraycopy(a, 0, elementData, index, numNew);
  55. size += numNew;
  56. return numNew != 0;
  57. }

ensureCapacityInternal

里面总共涉及到四个关键的方法

  1. private void ensureCapacityInternal(int minCapacity) {
  2. //先计算容量,再去扩容
  3. ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
  4. }
  5. //计算容量
  6. private static int calculateCapacity(Object[] elementData, int minCapacity) {
  7. //从这里可以看出,如果是通过无参构造创建的 ArrayList,那么第一次添加数据扩容的时候,容量最小为 10
  8. if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
  9. return Math.max(DEFAULT_CAPACITY, minCapacity);
  10. }
  11. return minCapacity;
  12. }
  13. private void ensureExplicitCapacity(int minCapacity) {
  14. modCount++;
  15. // overflow-conscious code
  16. //如果要扩充到的容量比现有的容量要大,才开始真正的扩容
  17. if (minCapacity - elementData.length > 0)
  18. grow(minCapacity);
  19. }
  20. private void grow(int minCapacity) {
  21. // overflow-conscious code
  22. int oldCapacity = elementData.length;
  23. //增加 50%的容量
  24. int newCapacity = oldCapacity + (oldCapacity >> 1);
  25. if (newCapacity - minCapacity < 0)
  26. //增加 50%后,还比需要的容量小,则新容量就等于所需要的容量。
  27. newCapacity = minCapacity;
  28. if (newCapacity - MAX_ARRAY_SIZE > 0)
  29. //如果计算出的新容量比最大值还要大,这里的 MAX_ARRAY_SIZE 为 Integer.MAX_VALUE - 8
  30. //则根据所需容量 minCapacity 来计算新容量
  31. newCapacity = hugeCapacity(minCapacity);
  32. //扩容
  33. elementData = Arrays.copyOf(elementData, newCapacity);
  34. }
  35. private static int hugeCapacity(int minCapacity) {
  36. if (minCapacity < 0) // overflow
  37. throw new OutOfMemoryError();
  38. //最大极限容量就是 Integer.MAX_VALUE
  39. return (minCapacity > MAX_ARRAY_SIZE) ?
  40. Integer.MAX_VALUE :
  41. MAX_ARRAY_SIZE;
  42. }

可以看出每次扩容最小都是增加原来的 50%,扩容的最大极限值就是 Integer.MAX_VALUE,如果最后一次扩容量不大于 MAX_ARRAY_SIZE,则直接扩容到 MAX_ARRAY_SIZE ,否则扩容到 Integer.MAX_VALUE

get

  1. public E get(int index) {
  2. rangeCheck(index);
  3. return elementData(index);
  4. }
  5. @SuppressWarnings("unchecked")
  6. E elementData(int index) {
  7. return (E) elementData[index];
  8. }

读取元素没啥好说的,检测 index 是否合法,然后直接返回数据。

remove、clear

  1. public E remove(int index) {
  2. rangeCheck(index);
  3. modCount++;
  4. E oldValue = elementData(index);
  5. int numMoved = size - index - 1;
  6. if (numMoved > 0)
  7. //如果要删除的数据不在最后一位,就将要删除的索引下标后面的数据往前移一位。
  8. System.arraycopy(elementData, index+1, elementData, index,
  9. numMoved);
  10. //然后将最后一位数据置空
  11. elementData[--size] = null; // clear to let GC do its work
  12. return oldValue;
  13. }
  14. public boolean remove(Object o) {
  15. if (o == null) {
  16. //如果移除的是 null,则遍历数组,将数组中第一个 null 移除
  17. for (int index = 0; index < size; index++)
  18. if (elementData[index] == null) {
  19. fastRemove(index);
  20. return true;
  21. }
  22. } else {
  23. //遍历数组,找到对应的下标,移除数据。
  24. for (int index = 0; index < size; index++)
  25. if (o.equals(elementData[index])) {
  26. fastRemove(index);
  27. return true;
  28. }
  29. }
  30. return false;
  31. }
  32. private void fastRemove(int index) {
  33. modCount++;
  34. int numMoved = size - index - 1;
  35. //如果移除的数据不是最后一个,则将下标右边的数据往左移一位,然后将最后一个数置为 null
  36. if (numMoved > 0)
  37. System.arraycopy(elementData, index+1, elementData, index,
  38. numMoved);
  39. elementData[--size] = null; // clear to let GC do its work
  40. }
  41. protected void removeRange(int fromIndex, int toIndex) {
  42. //这个方法的移除是包左不包右的 [fromIndex,toIndex) toIndex 最大可以为 size
  43. modCount++;
  44. int numMoved = size - toIndex;
  45. System.arraycopy(elementData, toIndex, elementData, fromIndex,
  46. numMoved);
  47. // clear to let GC do its work
  48. int newSize = size - (toIndex-fromIndex);
  49. for (int i = newSize; i < size; i++) {
  50. elementData[i] = null;
  51. }
  52. size = newSize;
  53. }
  54. public void clear() {
  55. modCount++;
  56. // clear to let GC do its work
  57. for (int i = 0; i < size; i++)
  58. elementData[i] = null;
  59. size = 0;
  60. }

其它方法

trimToSize

将 ArrayList 中的数组容量大小调整为实际元素的个数。

  1. public void trimToSize() {
  2. modCount++;
  3. if (size < elementData.length) {
  4. elementData = (size == 0)
  5. ? EMPTY_ELEMENTDATA
  6. : Arrays.copyOf(elementData, size);
  7. }
  8. }

toArray

ArrayList 转换为 数组

  1. public Object[] toArray() {
  2. //直接返回一个新数组
  3. return Arrays.copyOf(elementData, size);
  4. }
  5. @SuppressWarnings("unchecked")
  6. public <T> T[] toArray(T[] a) {
  7. if (a.length < size)
  8. // Make a new array of a's runtime type, but my contents:
  9. return (T[]) Arrays.copyOf(elementData, size, a.getClass());
  10. System.arraycopy(elementData, 0, a, 0, size);
  11. if (a.length > size)
  12. a[size] = null;
  13. return a;
  14. }

对于第二个方法来说如果传入的是数组长度小于 ArrayList 的长度,则返回一个新数组。如果大于等于 ArrayList 长度,则在传入的数组中进行赋值。size 的下标为 null,后面的数字不变。

  1. ArrayList<Integer> list1 = new ArrayList<>();
  2. list1.add(1);
  3. list1.add(2);
  4. list1.add(3);
  5. list1.add(4);
  6. list1.add(5);
  7. Integer[] a = new Integer[]{6,7,8,9,10,11,12,13,14};
  8. Integer[] integers = list1.toArray(a);
  9. System.out.println(Arrays.toString(integers));
  10. //------------------------ 输出结果 ------------------------
  11. [1, 2, 3, 4, 5, null, 12, 13, 14]

Arrays.copyof() 和 System.arraycopy()

Arrays.copyof() 有很多重载的方法,不过最终都调用了一个方法:

  1. public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
  2. @SuppressWarnings("unchecked")
  3. T[] copy = ((Object)newType == (Object)Object[].class)
  4. ? (T[]) new Object[newLength]
  5. : (T[]) Array.newInstance(newType.getComponentType(), newLength);
  6. System.arraycopy(original, 0, copy, 0,
  7. Math.min(original.length, newLength));
  8. return copy;
  9. }

首先创建一个长度为 newLength 的新数组,然后里面最终调用了 System.arraycopy 方法将原来数组中的元素赋值到新数组中,最后将新数组返回。

Vector 比较

  1. Vector 是线程安全的,增删改查几个方法都加入了 synchronized 关键字。
  2. Vector 的扩容量可以自定义,默认是 2 倍,ArrayList 是增加 50%。
  3. Vector提供indexOf(obj, start)接口,ArrayList没有。(indexOf(obj, start) 接口是从指定下标 start 开始查找 obj 所处的下标)
  4. Vector 的无参构造是刚开始就创建一个 10 容量的数组;ArrayList 是在添加元素的时候才开始检测如果是通过无参构造创建的,容量最小指定为 10。

    总结

  5. ArrayList 底层是数组结构,在增删操作上,如果不是最后一个元素,都会进行数组的复制操作,耗时较久。但是在查询方面可以直接通过下标索引查找到指定的元素,查询速度快。

  6. ArrayList 通过无参构造创建时,默认是一个容量为 0 的空数组,在后续操作需要扩容的时候,如果发现是通过无参构造创建的,那么扩容的最小容量为 10。(对于 jdk 1.8以前的源码里面是直接创建一个 10 容量的初始数组,这点和 Vector 一样)
  7. EMPTY_ELEMENTDATADEFAULT_CAPACITY 都是空数组,EMPTY_ELEMENTDATA 代表 ArrayList 是通过无参构造创建的,DEFAULT_CAPACITY 代表后续操作 ArrayList 为空时所指定的数组。两者区分开来,就是为了第一次扩容时,确保容量大小最小为 10。
  8. ArrayList 允许元素为 null。