Java ArrayList

构造函数

ArrayList 有三个构造函数,默认不带参数的构造函数就是初始化一个空数组。

  1. //一个空数组
  2. private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
  3. //实际存储元素的数组
  4. transient Object[] elementData;
  5. public ArrayList() {
  6. this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
  7. }

带一个 int 类型的容量参数的构造函数,可以指定 ArrayList 的容量大小。

  1. //空数组
  2. private static final Object[] EMPTY_ELEMENTDATA = {};
  3. public ArrayList(int initialCapacity) {
  4. if (initialCapacity > 0) {
  5. //容量大于 0 就构建一个 Object 的数组
  6. this.elementData = new Object[initialCapacity];
  7. } else if (initialCapacity == 0) {
  8. //容量等于 0 就是一个空数组
  9. this.elementData = EMPTY_ELEMENTDATA;
  10. } else {
  11. //容量小于 0 抛出异常
  12. throw new IllegalArgumentException("Illegal Capacity: "+
  13. initialCapacity);
  14. }
  15. }

带一个集合的构造函数, 将集合转换成 Object[] 类型后拷贝到 elementData 中。

  1. public ArrayList(Collection<? extends E> c) {
  2. //集合转为数组
  3. Object[] a = c.toArray();
  4. if ((size = a.length) != 0) {
  5. //集合是不是 ArrayList 类型
  6. if (c.getClass() == ArrayList.class) {
  7. elementData = a;
  8. } else {
  9. //将集合元素拷贝成 Object[] 到 elementData
  10. elementData = Arrays.copyOf(a, size, Object[].class);
  11. }
  12. } else {
  13. //空元素
  14. elementData = EMPTY_ELEMENTDATA;
  15. }
  16. }

常用函数

add()

先从 ArrayList 最常用的方法 add() 开始讲起,add() 方法就是将元素添加到 elementData 的末尾。平均时间复杂度为 O(1)。

  1. //ArrayList.add()
  2. public boolean add(E e) {
  3. //检查数组是否存放的下
  4. ensureCapacityInternal(size + 1);
  5. //添加到末尾
  6. elementData[size++] = e;
  7. return true;
  8. }
  9. //ArrayList.ensureCapacityInternal()
  10. private void ensureCapacityInternal(int minCapacity) {
  11. ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
  12. }
  13. //ArrayList.calculateCapacity()
  14. private static int calculateCapacity(Object[] elementData, int minCapacity) {
  15. //检查时不是默认时的空数组,是默认时的空数组,初始化的容量就是 10
  16. if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
  17. return Math.max(DEFAULT_CAPACITY, minCapacity);
  18. }
  19. return minCapacity;
  20. }
  21. //ArrayList.ensureExplicitCapacity()
  22. private void ensureExplicitCapacity(int minCapacity) {
  23. modCount++;
  24. if (minCapacity - elementData.length > 0)
  25. grow(minCapacity);
  26. }
  27. //最大容量
  28. private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
  29. //ArrayList.grow()
  30. private void grow(int minCapacity) {
  31. int oldCapacity = elementData.length;
  32. //新长度时原来长度的 1.5 倍
  33. int newCapacity = oldCapacity + (oldCapacity >> 1);
  34. //新长度小于实际容量,就用实际容量
  35. if (newCapacity - minCapacity < 0)
  36. newCapacity = minCapacity;
  37. //新长度太大了,就用最大的容量
  38. if (newCapacity - MAX_ARRAY_SIZE > 0)
  39. newCapacity = hugeCapacity(minCapacity);
  40. //copy 成一个新数组
  41. elementData = Arrays.copyOf(elementData, newCapacity);
  42. }
  1. 扩容的大小为原长度+1/2的原长度
  2. 如果扩容长度比传入的最小容量小,则使用最小容量,如果扩容长度超过设定的最大容量,则实际用最大容量
  3. 初始化默认长度为10,当添加到11个长度时,容量为15

    add(int index, E element)

    将元素插入到指定的位置,主要的操作是将指定位置之后的元素往后移动一个位置,空出 index 位置。平均时间复杂度为 O(n)。

    1. //ArrayList.add(int index, E element)
    2. public void add(int index, E element) {
    3. //index的越界检查
    4. rangeCheckForAdd(index);
    5. //扩容
    6. ensureCapacityInternal(size + 1);
    7. //将 index 之后的所有元素 copy 到往后挪动一位
    8. System.arraycopy(elementData, index, elementData, index + 1,
    9. size - index);
    10. //将 index 位置放入新元素
    11. elementData[index] = element;
    12. //数量 + 1
    13. size++;
    14. }

    get()

    get() 应该是第二个常用的函数,可以返回随机位置的元素。需要注意的是越界时,超过容量返回的是 IndexOutOfBoundsException 异常,index 太小返回的是 ArrayIndexOutOfBoundsException 异常。平均时间复杂度为 O(1)。 ```java //ArrayList.get() public E get(int index) { //index 越界检查 rangeCheck(index); //返回指定位置的元素 return elementData(index); }

//ArrayList.rangeCheck() private void rangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } //ArrayList.elementData() E elementData(int index) { return (E) elementData[index]; }

  1. <a name="HLAUU"></a>
  2. #### `remove()`
  3. 删除指定位置的元素,并返回删除的元素。平均时间复杂度为 O(n)。
  4. ```java
  5. //ArrayList.remove()
  6. public E remove(int index) {
  7. //越界检查
  8. rangeCheck(index);
  9. modCount++;
  10. //取出元素
  11. E oldValue = elementData(index);
  12. //拷贝数组,将 index 之后的元素往前移动一个位置
  13. int numMoved = size - index - 1;
  14. if (numMoved > 0)
  15. System.arraycopy(elementData, index+1, elementData, index,
  16. numMoved);
  17. //最后一个元素置 null
  18. elementData[--size] = null;
  19. return oldValue;
  20. }

remove(Object o)

删除指定的元素,需要循环数组删除第一个指定的元素。平均时间复杂度为 O(n)。

  1. //ArrayList.remove(Object o)
  2. public boolean remove(Object o) {
  3. if (o == null) {
  4. //循环整个数组,找出第一个需要删除的元素
  5. for (int index = 0; index < size; index++)
  6. if (elementData[index] == null) {
  7. fastRemove(index);
  8. return true;
  9. }
  10. } else {
  11. //循环整个数组,找出第一个需要删除的元素
  12. for (int index = 0; index < size; index++)
  13. if (o.equals(elementData[index])) {
  14. fastRemove(index);
  15. return true;
  16. }
  17. }
  18. return false;
  19. }
  20. //ArrayList.fastRemove
  21. private void fastRemove(int index) {
  22. modCount++;
  23. int numMoved = size - index - 1;
  24. if (numMoved > 0)
  25. System.arraycopy(elementData, index+1, elementData, index,
  26. numMoved);
  27. elementData[--size] = null;
  28. }

总结

2021-09-04-13-14-17-090662.png

  1. ArrayList 内部用一个数组存储元素,容量不够时会扩容原来的一半。
  2. ArrayList 实现了 RandomAccess,支持随机访问。
  3. ArrayList 实现了 Cloneable,支持被拷贝克隆。
  4. ArrayList 删除指定元素和指定位置上的元素的效率不高,需要遍历数组。