ArrayList

类图

image.png

  • List: 提供增、删、迭代遍历等方法要求
  • RandomAccess: 提供可随机访问的功能
  • Serializable: 提供可序列化的功能
  • AbstractList: 提供了 List 接口的骨架实现,尤其是实现 迭代遍历 相关的代码
    • 不过对于 ArrayList 来说没啥用,前者又重写了很多方法

  • 几个概念

  1. index: 数组下标
  2. transient Object[] elementData; 数组本身,默认为 null
  3. private int size; 已使用了 elementData 的数量,没有 volatile 修饰, 非线程安全
  4. private static final int DEFAULT_CAPACITY = 10; 初始容量,默认 10
  5. protected transient int modCount = 0; 版本号,当前数组被修改的版本次数,数组结构变动,就会 +1

初始化

  1. 无参构造,则 elementData 依然为 null
    1. 需要进行 内部扩容
  2. 有参构造,接收 ```java // 空数组, 首次扩容为 10 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 空数组,扩容会按照 1.5 倍扩容 private static final Object[] EMPTY_ELEMENTDATA = {};

//无参数直接初始化,数组大小为空 public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }

// 指定大小初始化 public ArrayList(int initialCapacity) { if (initialCapacity > 0) { // 初始化数组 this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { // 为0,则为空数组 this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException(“Illegal Capacity: “+ initialCapacity); } }

//指定初始数据初始化 public ArrayList(Collection<? extends E> c) { //elementData 是保存数组的容器,默认为 null elementData = c.toArray(); //如果给定的集合(c)数据有值 if ((size = elementData.length) != 0) { // c.toArray might (incorrectly) not return Object[] (see 6260652) // 如果集合元素类型不是 Object 类型,我们会转成 Object if (elementData.getClass() != Object[].class) { elementData = Arrays.copyOf(elementData, size, Object[].class); } } else { // 给定集合(c)无值,则默认空数组 this.elementData = EMPTY_ELEMENTDATA; } }

  1. - `<A>`: jdk9 修复 bug
  2. <a name="39AZB"></a>
  3. ## 增加
  4. ```java
  5. public boolean add(E e) {
  6. // <A>
  7. ensureCapacityInternal(size + 1); // Increments modCount!!
  8. // <B>
  9. elementData[size++] = e;
  10. return true;
  11. }
  • <A>: 判断是否需要扩容,传入 请求索引值
  • <B>: 将元素进行添加,此处线程不安全

内部扩容

  • 如果 当前list 是无参构造而来的

    • 那么需要进行内部扩容,将其扩容为 10
      1. private void ensureCapacityInternal(int minCapacity) {
      2. // <A>
      3. if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
      4. minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
      5. }
      6. // <B>
      7. ensureExplicitCapacity(minCapacity);
      8. }
  • <A>: 内部扩容 如果是空数组,则在 minCapacity(size + 1 [对应 add()] 或者 size + number 对应 addAll()) 和 DEFAULT_CAPACITY(默认值,10) 中取最大值

    • 一般结果是设置为 DEFAULT_CAPACITY,即 10
  • <B>: 进行扩容判断

扩容判断

  1. private void ensureExplicitCapacity(int minCapacity) {
  2. // <A>
  3. modCount++;
  4. // overflow-conscious code
  5. // <B>
  6. if (minCapacity - elementData.length > 0)
  7. grow(minCapacity);
  8. }
  • <A>: 版本号 + 1
  • <B>: 如果 最小容量 比当前数组长度大,则进行扩容

扩容

  1. private void grow(int minCapacity) {
  2. // 1
  3. int oldCapacity = elementData.length;
  4. // 2
  5. int newCapacity = oldCapacity + (oldCapacity >> 1);
  6. // 3
  7. if (newCapacity - minCapacity < 0)
  8. newCapacity = minCapacity;
  9. // 4
  10. if (newCapacity - MAX_ARRAY_SIZE > 0)
  11. // 4.a
  12. newCapacity = hugeCapacity(minCapacity);
  13. // minCapacity is usually close to size, so this is a win:
  14. // 5
  15. elementData = Arrays.copyOf(elementData, newCapacity);
  16. }
  1. 获取当前数组长度
  2. 获取预期扩容值,数字为当前数组长度*1.5的 (>>1 表示除以 2)
  3. 如果预期扩容值 小于 请求索引值,直接设置预期扩容值为请求索引值
    • 预期扩容值要么 == elementData.length, 要么 == minCapacity
  4. 如果预期扩容值 大于 jvm 所能分配的数组的最大值( Integer.MAX_VALUE - 8 ) 则进行 <DD>
    1. 预计扩容值设置为 Integer.MAX_VALUE
  5. 真正的扩容,底部调用 System.arraycopy 进行拷贝

删除

  1. // 按值删除
  2. public boolean remove(Object o) {
  3. if (o == null) {
  4. // 1.
  5. for (int index = 0; index < size; index++)
  6. // 1.a
  7. if (elementData[index] == null) {
  8. fastRemove(index);
  9. return true;
  10. }
  11. } else {
  12. // 2.
  13. for (int index = 0; index < size; index++)
  14. // 2.a
  15. if (o.equals(elementData[index])) {
  16. fastRemove(index);
  17. return true;
  18. }
  19. }
  20. return false;
  21. }
  1. 如果值为 null, 则在 elementData[index, size) 中查找值为 null 的位置,进行 快速删除
    1. 进行 elementData[index] == null 判断
  2. 如果值不为 null,则在 elementData[index, size) 中查找指定值的位置,进行 快速删除
    1. 进行 指定值.equals(elementData[index] 判断

快速删除

  1. private void fastRemove(int index) {
  2. // 1.
  3. modCount++;
  4. // 2.
  5. int numMoved = size - index - 1;
  6. // 2.a
  7. if (numMoved > 0)
  8. System.arraycopy(elementData, index+1, elementData, index,
  9. numMoved);
  10. // 3
  11. elementData[--size] = null; // clear to let GC do its work
  12. }
  1. 版本号 + 1
  2. 计算往前挪的数值
    1. 如果步骤2中的数值大于0,说明需要对数组进行向前整体移动
    2. 否则说明将移除的 index 是最后一位
  3. 将数组的最后一位置 null,且 size - 1
  • 步骤2 和 步骤3 可以看成
    • 如果

迭代器

  1. private class Itr implements Iterator<E> {
  2. int cursor; // index of next element to return
  3. int lastRet = -1; // index of last element returned; -1 if no such
  4. int expectedModCount = modCount;
  5. // other method
  6. }

参数

  1. int cursor; // index of next element to return 迭代中的下个元素的索引,默认 0
  2. int lastRet = -1; // index of last element returned; -1 if no such 迭代中最后一个元素的索引, -1 表示么得了
  3. int expectedModCount = modCount; // 迭代中期望的版本号

hasNext

  1. public boolean hasNext() {
  2. return cursor != size;
  3. }
  • 迭代中的下一个元素的索引不为数组的大小,返回 true

next

  1. public E next() {
  2. // 1.
  3. checkForComodification();
  4. // 2.
  5. int i = cursor;
  6. if (i >= size)
  7. throw new NoSuchElementException();
  8. // 3.
  9. Object[] elementData = ArrayList.this.elementData;
  10. if (i >= elementData.length)
  11. throw new ConcurrentModificationException();
  12. // 4
  13. cursor = i + 1;
  14. // 5.
  15. return (E) elementData[lastRet = i];
  16. }
  1. 检查版本号
  2. 获取 cursor 的值
  3. 获取当前数组
  4. cursor +1
  5. 返回 elementData[curosr], 并且设置 lastRet 为最新的 cursor

    remove

    ```java public void remove() { // 1. if (lastRet < 0)
    1. throw new IllegalStateException();
    // 2. checkForComodification();
  1. try {
  2. // 3.
  3. ArrayList.this.remove(lastRet);
  4. // 4.
  5. cursor = lastRet;
  6. lastRet = -1;
  7. expectedModCount = modCount;
  8. } catch (IndexOutOfBoundsException ex) {
  9. throw new ConcurrentModificationException();
  10. }

}

```

  1. lastRet 小于 0, 说明是没有元素了
  2. 检查版本号
  3. 删除该元素
  4. // 删除元素时 modCount 的值已经发生变化,在此赋值给 expectedModCount

    // 这样下次迭代时,两者的值是一致的了