java集合(九)---ArrayList源码分析

一、关于ArrayList的图如下图所示:

,WNA完全在这里插入图片描述
从图中可以看出,ArrayList是List的一个典型实现,完全支持List接口的全部功能。

二、关于源码:

ArrayList构造函数:
在这里插入图片描述
成员方法解析:

1. public ArrayList(int initialCapacity)带参构造方法

    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);
        }
    }

源码解析:

  • 功能: 带参数构造函数,初始化数组的容量

  • 源码思路:

    • (1)判断传递进来的容量,容量值与0相比较,如果大于0,new一个Object类型的数组,将其赋给elementData
    • (2)如果容量值等于0,说明想要初始化的数组容量为空,那直接将EMPTY_ELEMENTDATA赋给elementData
    • (3)如果容量值小于0,则不符合题意,抛出异常

2. public ArrayList()空参构造方法

     public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

源码解析:

  • 功能:初始化数组元素,因为没有参数,所以默认数组的元素个数为0,将DEFAULTCAPACITY_EMPTY_ELEMENTDATA赋给elementData

3. public ArrayList(Collection<? extends E> c)方法

    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;
            }
}

源码解析:

  • 功能: 初始化ArrayList元素,使用已知传递进来的元素进行初始化
  • 源码思路:
    • (1)调用Collection类型形式参数c的toArray方法,将集合元素编程数组元素,存储在elementData中
    • (2)将elementData数组的长度值赋给size,判断如果size的值不等于0,则再继续判断如果elementData数组的class值是否等于Object类型的数组的class,如果不相等,使用Arrays类的copyOf方法,给elementData数组赋初值
    • (3)如果传递进来的集合元素为null,则将elementData数组赋为初始空数组EMPTY_ELEMENTDATA

4. public void trimToSize()方法

    public void trimToSize() {
        modCount++;
        if (size < elementData.length) {
            elementData = (size == 0)
              ? EMPTY_ELEMENTDATA
              : Arrays.copyOf(elementData, size);
        }
    }

源码解析:

  • 功能:修整此ArrayList实例的是列表的当前大小的容量。ArrayList所说没有用的值并不是null,而是ArrayList每次增长会申请多一点的空间,增长1.5倍+1,而不是两倍这样就会出现当size()= 1000的时候,ArrayList已经申请了1200空间的情况trimToSize 的作用只是去掉预留元素位置,就是删除多余的200,改为只申请1000,内存紧张的时候会用到 。
  • 源码思路:
    • 修改的方式是通过调用Arrays类的copyOf方法

5. public void ensureCapacity(int minCapacity)方法

    public void ensureCapacity(int minCapacity) {
        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            ? 0 : DEFAULT_CAPACITY;

        if (minCapacity > minExpand) {
            ensureExplicitCapacity(minCapacity);
        }
}

源码解析:

  • 功能:如果需要,增加此ArrayList实例的容量,以确保它至少可以容纳由最小容量参数指定的元素数。

  • 源码思路:

    • (1)任何一个ArrayList对象都有一个capacity属性,用来指示该ArrayList的最小容量,即数组的“容纳能力”,我们知道ArrayList的内部是采用数组来存储元素的,由于Java数组是定长的,所以这个数组的大小一定是固定的,这个大小就是capacity。我们可以肯定capacity一定是大于或者等于ArrayList的size,那么当size不断增加到了要超过capacity的时候,ArrayList就不得不重新创建新的capacity来容纳更多的元素,这时需要首先建立一个更长的数组,将原来的数组中的元素赋值到新数组中,再删除原来的数组。因此当ArrayList越来越大时,这种操作的消耗也就越来越大。

    • (2)为了减少这种不必要的重建capacity操作,当我们能肯定ArrayList大致有多大时,我们可以先让ArrayList把capacity设为我们期望的大小,以避免多余的数组重建

    • (3)建设ArrayList自动把capacity设为10,每次重建时将长度递增原来的三分之二,那么当我们需要大约存储50个元素到ArrayList中时,就会大约需要重建数组4次,分别在增加第11、第17、第26、第39个元素的时候进行。如果我们一开始就让ArrayList的capacity为50,那么不需要任何数组重建就能完成所有插入操作了。

    • (4)Java允许我们在构造ArrayList的同时指定capacity,如new
      ArrayList(50),也允许在以后将它设得更大,而增大capacity就是使用ensureCapacity()方法。注意:capacity只能比原来的更大,不能比原来的更小,否则Java会忽略该操作。ArrayList的初始默认capacity为10,所以给capacity指定小于10的整数毫无意义。

    • (5)ArrayList的size一定小于等于capacity,而且更重要的是,访问超过size的位置将抛出异常,尽管这个位置可能没有超过capacity。ensureCapacity()只可能增加capacity,而不会对size有任何影响。要增加size,只能用add()方法。

6. private static int calculateCapacity(Object[] elementData, int minCapacity)方法

    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
}

源码解析:

  • 功能:计算最小容量
  • 源码思路:
    • (1)判断如果elementData的值等于默认空数组的值,那么返回DEFAULT_CAPACITY和minCapacity中的最大值
    • (2)如果elementData的值不等于默认空数组的值,则返回minCapacity值

7. private void ensureCapacityInternal(int minCapacity)方法

  private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

源码解析:

  • 功能:扩容入口方法

8. private void ensureExplicitCapacity(int minCapacity)方法

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
}

源码解析:

  • 功能:判断是否需要扩容
  • 源码思路:
    • 首先要更改modCount的值
    • 如果最小需要空间比当前的elementData的长度大,则需要扩容,调用grow扩容方法,将minCapacity传递进去

9. private void grow(int minCapacity)方法

    private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

源码解析:

  • 功能:动态扩容
  • 源码思路:
    • (1)定义一个变量oldCapacity,其值等于ArrayList中elementData数组的长度
    • (2)定义一个变量newCapacity,其值等于ArrayList中elementData数组长度的1.5倍
    • (3)如果新数组的容量小于预留的数组空间,就可以直接使用这个长度新建数组
    • (4)如果新数组的容量大于预留的数组空间,就将预留数组长度设置为需要的长度
    • (5)MAX_ARRAY_SIZE
      值等于Java中int的最大值。下面要判断有没有超过最大限制,如果没有,就执行下一步,如果超过最大限制,需要修改最大限制
    • (6)调用Arrays的copyOf方法将elementData数组指向新的内存空间newCapacity,并将elementData数组中的值传递到新的内存空间中

10. private static int hugeCapacity(int minCapacity) 方法

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

源码解析:

  • 功能:数组能够容纳的最大数据元素个数
  • 源码思路:
    • (1)如果数组能够容纳数据的能力小于0,说明minCapacity是错误的,因此抛出异常
    • (2)如果minCapacity的值大于数组的size,则返回整数的最大值,否则返回数组的size

11. public int size()方法

    public int size() {
        return size;
    }

源码解析:

  • 功能:获取数组的size大小

12. public boolean isEmpty()方法

    public boolean isEmpty() {
        return size == 0;
    }

源码解析:

  • 功能:判断数组中是否有元素
  • 源码思路:
    • 判断size的值是否为0

13. public boolean contains(Object o)方法

    public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }

源码解析:

  • 功能:判断一个元素是否存在在数组中
  • 源码思路:
    • 通过调用indexOf方法来实现该功能

14. public int indexOf(Object o)方法

 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; } 

源码解析:

  • 功能:从左到右判断元素是否在数组中,如果在,则返回元素的下标,如果不在返回-1,直到找到或者到数组末尾为止
  • 源码思路:
    • 分两种情况进行判断,一种是元素是null,另一种是元素非空
    • 当元素是null时,通过for循环遍历数组,当找到数组中元素==null则返回元素下标i
    • 当元素是非空时,通过for循环遍历数组,通过equals方法来判断数组中是否存在想找的元素

15. public int lastIndexOf(Object o)方法

 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; } 

源码解析:

  • 功能:从右往左判断是否有元素o,如果有则返回元素的下标,否则返回-1
  • 源码思路:
    • 与indexOf方法相反

16. public Object clone()方法

 public Object clone() { try { ArrayList<?> v = (ArrayList<?>) super.clone(); v.elementData = Arrays.copyOf(elementData, size); v.modCount = 0; return v; } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable throw new InternalError(e); } } 

源码解析:

  • 功能:克隆
  • 源码思路:
    • (1)调用super.clone
    • (2)将elementData中的元素克隆到新的elementData中
    • (3)将新的集合的modCount 值设置为0

17. public Object[] toArray()方法

 public Object[] toArray() { return Arrays.copyOf(elementData, size); } 

源码解析:

  • 功能:将集合元素变为Object类型的数组返回
  • 源码思路:
    • 实现的方式是通过Arrays类的copyOf方法实现

18. public T[] toArray(T[] a)方法

 public <T> T[] toArray(T[] a) { if (a.length < size) return (T[]) Arrays.copyOf(elementData, size, a.getClass()); System.arraycopy(elementData, 0, a, 0, size); if (a.length > size) a[size] = null; return a; } 

源码解析:

  • 功能:将集合中的元素转换为泛型类型的数组,存储在参数中返回
  • 源码思路:
    • (1)如果返回的参数a的数组长度小于集合的size,说明参数不能够存储下集合中的元素,直接通过调用Arrays类的copyOf方法,将调用结果返回
    • (2)如果参数a的数组长度大于集合的size,说明参数能够存储下集合中的元素,调用System类的arraycopy方法将集合中的元素放在数组中,因为a的存储空间有多余的部分,需要最后将下标为size的元素置为null,最后返回a

19. E elementData(int index)方法

 E elementData(int index) { return (E) elementData[index]; } 

源码解析:

  • 功能:返回elementData数组下标为index的元素值,get方法的基础方法
  • 源码思路:
    • 因为elementData本身是数组,所以想返回下标index的元素值,直接利用数组的elementData[index]即可

20. public E get(int index)方法

 public E get(int index) { rangeCheck(index); return elementData(index); } 

源码解析:

  • 功能:返回集合中下标为index的元素
  • 源码思路:
    • 首先调用rangeCheck方法判断index是否符合题意
    • 返回调用elementData方法的结果

21. public E set(int index, E element)方法

 public E set(int index, E element) { rangeCheck(index); E oldValue = elementData(index); elementData[index] = element; return oldValue; } 

源码解析:

  • 功能:将集合中下标为index的元素值换为element
  • 源码思路:
    • (1)首先调用rangeCheck方法判断index下标是否符合条件
    • (2)调用elementData方法获取下标为index的元素值,将该值放在oldValue中
    • (3)将element值赋给elementData[index]中
    • (4)将旧值返回

22. public boolean add(E e)方法

 public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } 

源码解析:

  • 功能: 将元素e添加到集合中
  • 源码思路:
    • (1)首先调用ensureCapacityInternal方法,扩充数组的容量
    • (2)将将e的值添加到elementData数组中

23. public void add(int index, E element)方法

 public void add(int index, E element) { rangeCheckForAdd(index); ensureCapacityInternal(size + 1); // Increments modCount!! System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; } 

源码解析:

  • 功能:向数组下标为index的位置,插入元素element
  • 源码思路:
    • (1)首先调用rangeCheckForAdd方法,判断index是否符合条件
    • (2)调用ensureCapacityInternal方法,扩充数组的容量
    • (3)将数组中index处及其后面的值向后移动1位
    • (4)将element的值赋值给elementData数组的index下标位置
    • (5)增加size的大小

24. public E remove(int index)方法

 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; } 

源码解析:

  • 功能:将下标为index位置的元素移除
  • 源码思路:
    • (1)首先调用rangeCheck方法,用来判断index是否符合条件
    • (2)更改modCount的值
    • (3)调用elementData方法,获取index位置的元素值,放在oldValue变量中
    • (4)定义变量numMoved,其值等于size - index - 1,即将第index+1个元素及其后面的元素向前移动
    • (5)将最后一个元素赋值为null
    • (6)返回删除的元素的信息

25. public boolean remove(Object o)方法

 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; } 

源码解析:

  • 功能:从左向右一次遍历,将第一次出现的元素o从集合中删除
  • 源码思路:
    • 分两种情况进行判断,一种是o为null,另一种是o非null

26. private void fastRemove(int index)方法

 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 } 

源码解析:

  • 功能:将第index元素从集合中删除
  • 源码思路:
    • (1)首先更改modCount值,因为每从结构上修改一次集合,就要将modCount的值加1
    • (2)定义一个变量numMoved,其值等于将要移动的元素的个数
    • (3)利用System类的arraycopy方法将第index+1及其后面的元素向前移动一位,即将第index元素覆盖(注意:是直接将index位置的元素覆盖,而不是返回其元素
    • (4)将集合最后一个元素赋为null

27. public void clear()方法

 public void clear() { modCount++; // clear to let GC do its work for (int i = 0; i < size; i++) elementData[i] = null; size = 0; } 

源码解析:

  • 功能:将集合中的元素全部删除
  • 源码思路:
    • (1)首先修改modCount的值
    • (2)利用for循环将集合中的元素全部置于null
    • (3)将size的值置为0

28. public boolean addAll(Collection<? extends E> c)方法

 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; } 

源码解析:

  • 功能:将集合中的元素添加到现有的集合中
  • 源码思路:
    • (1)定义一个Object类型的数组a,将集合c调用toArray方法,将集合c转换为数组,放在a中
    • (2)定义一个变量numNew,用来存储数组a的长度
    • (3)调用ensureCapacityInternal方法,扩充modCount
    • (4)调用System类的arraycopy方法,将数组a中的元素copy到elementData中,数组a从下标0开始复制,复制到elementData中的下标size位置,复制numNew个元素
    • (5)最后更改size的值

29. public boolean addAll(int index, Collection<? extends E> c)方法

 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; } 

源码解析:

  • 功能:将集合中的元素添加到现有的集合中,增加一个添加的下标位置
  • 源码思路:
    • 与弟28个函数是相似的

30. protected void removeRange(int fromIndex, int 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; } 

源码解析:

  • 功能:将集合中的元素从下标fromIndex开始到下标toIndex移除
  • 源码思路:
    • (1)更改modCount的值,将该值自增1
    • (2)定义变量numMoved,该变量的值等于size - toIndex
    • (3)调用System类的arraycopy方法,将elementData数组中位置toIndex的值及其后面的值复制到elementData数组的fromIndex位置
    • (4)记着将newSize以后的值置为null
    • (5)更改size的值

31. private void rangeCheck(int index)方法

 private void rangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } 

源码解析:

  • 功能: 判断index是否符合条件
  • 源码思路:
    • 如果index的值大于等于size的值,即下标的值大于数组的最大值,要抛出异常

32. private void rangeCheckForAdd(int index)方法

 private void rangeCheckForAdd(int index) { if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } 

源码解析:

  • 功能:判断将要插入的元素的位置是否符合条件
  • 源码思路:
    • 插入的元素位置需要小于size大于0才符合条件,否则抛出异常

33. private String outOfBoundsMsg(int index)方法

 private String outOfBoundsMsg(int index) { return "Index: "+index+", Size: "+size; } 

源码解析:

  • 功能:抛出异常时返回的出错信息
  • 源码思路:
    • 返回元素的位置和集合的大小

34. public boolean removeAll(Collection<?> c)方法

 public boolean removeAll(Collection<?> c) { Objects.requireNonNull(c); return batchRemove(c, false); } 

源码解析:

  • 功能:移除当前集合中集合c中所有元素
  • 源码思路:
    • (1)首先调用Objects类的requireNoNull方法,该方法用来判断c是否为null,如果为null则抛出异常,如果不为空,则返回c本身
    • (2)移除的操作是调用batchRemove方法实现

35. public boolean retainAll(Collection<?> c)方法

 public boolean retainAll(Collection<?> c) { Objects.requireNonNull(c); return batchRemove(c, true); } 

源码解析:

  • 功能:保留当前集合中所有集合c中的元素
  • 源码思路:
    • (1)调用Objects类的requireNonNull方法,判断c是否为空
    • (2)调用batchRemove方法来实现保存操作

36. private boolean batchRemove(Collection<?> c, boolean complement)方法

 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; } 

源码解析:

  • 功能:批量删除元素,利用complement来判断是删除参数c中的元素,还是保留c中的元素。complement为true,则保留c中的元素,为false则删除c中的元素
  • 源码思路:
    • (1)定义一个Object类型的数组elementData,将当前集合的elementData值赋值给新定义的elementData
    • (2)通过for循环来修改当前集合中的元素信息,如果c中不包含elementData[r]这个元素,则直接将r位置的元素赋值给w位置的元素,w自增
    • (3)在finally中,防止抛出异常导致上面r的右移过程没完成 , 将r未右移完成的位置的元素赋值给w右边位置的元素 ,修改w值增加size-r
    • (4)如果w不等于size,说明有被覆盖掉的元素,则将w后面的元素都赋值为null ,最后修改size的值

37. public ListIterator listIterator(int index)方法

 public ListIterator<E> listIterator(int index) { if (index < 0 || index > size) throw new IndexOutOfBoundsException("Index: "+index); return new ListItr(index); } 

源码解析:

  • 功能:创建ArrayList的迭代器,迭代器有起始位置
  • 源码思路:
    • (1)首先判断index值是否符合条件,如果不符合条件则抛出异常
    • (2)如果符合条件,new一个ListItr,将index值作为参数传递进去,迭代器初始位置为index

38. public ListIterator listIterator()方法

 public ListIterator<E> listIterator() { return new ListItr(0); } 

源码解析:

  • 功能:创建ArrayList的迭代器,迭代器没有起始位置
  • 源码思路:
    • 直接返回ListItr,迭代器初始位置为0

39. public Iterator iterator()方法

 public Iterator<E> iterator() { return new Itr(); } 

源码解析:

  • 功能:重写父类的iterator方法
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值