[Java集合源码] 一步一步解析 ArrayList 中常用方法的源码

本文详细探讨了Java中的ArrayList数据结构。首先介绍了ArrayList默认容量为10,可通过无参构造函数创建。其次,展示了如何使用已有集合初始化ArrayList。接着讲解了ensureCapacity方法在扩容方面的实现,确保能容纳特定数量的元素。最后,提到了几个关键方法,如contains、indexOf和lastIndexOf的工作原理。
摘要由CSDN通过智能技术生成

1. 默认数组大小为10,在构造函数不传入参数的时候使用默认大小

// ArrayList继承了AbstractList,实现了RandomAccess、Cloneable和java.io.Serializable
 public class ArrayList<E> extends AbstractList<E>
         implements List<E>, RandomAccess, Cloneable, java.io.Serializable
 {
 ​
     // 默认数组大小
     private static final int DEFAULT_CAPACITY = 10;
 ​
     // 默认的空数组,初始化时不传入初始化大小默认是一个空数组
     private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
 ​
     // ArrayList的底层结构,本质上是一个数组
     transient Object[] elementData;
 ​
     // 初始化时不传入初始化大小默认是一个空数组,默认大小为10
     public ArrayList() {
         this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
     }
 }

2. 可以使用另外一个集合,在构建ArrayList时作为参数传递到构造函数中,来初始化ArrayList:

    public ArrayList(Collection<? extends E> c) {
         elementData = c.toArray();
         // 集合c大小不为0
         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;
         }
     }

3. ensureCapacity 判断是否需要扩容,以保证数组能hold住minCapacity个元素     

    public void ensureCapacity(int minCapacity) {
         int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
             // any size if not default element table 如果数组不是默认的数组(即{}),则为0
             ? 0
             // larger than default for default empty table. It's already
             // supposed to be at default size.
             // 如果数组等于默认数组,则为DEFAULT_CAPACITY(值为10)
             : DEFAULT_CAPACITY;
 ​
        // 判断minCapacity是否大于需要扩容的最小值
         if (minCapacity > minExpand) {
             ensureExplicitCapacity(minCapacity);
         }
     }
 ​
     private void ensureExplicitCapacity(int minCapacity) {
         // the number of times this list has been structurally modified,
         // Structural modifications are those that change the size of the list
         modCount++;
 ​
         // 如果设置的最小容量比真实的数组大小大,则调用grow扩容
         if (minCapacity - elementData.length > 0)
             grow(minCapacity);
     }
 ​
     /**
      * Increases the capacity to ensure that it can hold at least the
      * number of elements specified by the minimum capacity argument.
      * 扩容以保证数组能够放下minCapacity个元素
      *
      * @param minCapacity the desired minimum capacity
      */
     private void grow(int minCapacity) {
         // 原数组大小
         int oldCapacity = elementData.length;
         // 新数组大小,为原大小的1.5倍
         int newCapacity = oldCapacity + (oldCapacity >> 1);
         // 如果新数组的大小比设定的minCapacity小,则新数组大小设为minCapacity
         if (newCapacity - minCapacity < 0)
             newCapacity = minCapacity;
         // 如果新数组的大小比默认最大数组大小(Integer.MAX_VALUE - 8)大,则调用hugeCapacity方法
         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);
     }
     
     // 当newCapacity大于默认最大数组大小时调用
     private static int hugeCapacity(int minCapacity) {
         if (minCapacity < 0) // overflow
             throw new OutOfMemoryError();
         // 如果要设定的最小容量比默认数组最大容量大,则新数组的大小设为整数最大值,否则新数组的
         // 大小设为默认数组最大容量(Integer.MAX_VALUE - 8)
         return (minCapacity > MAX_ARRAY_SIZE) ?
             Integer.MAX_VALUE :
             MAX_ARRAY_SIZE;
     }

4. 两个简单的获取属性的方法,直接通过属性值得到 ​      

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

5. contains,indexOf,lastIndexOf方法 

    public boolean contains(Object o) {
         return indexOf(o) >= 0;
     }
 ​
     public int indexOf(Object o) {
         // 如果要查的元素为null,则遍历数组找出元素为null的下标,
         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;
         }
         
         // 如果找不到直接返回-1
         return -1;
     }
 ​
     // 查找逻辑与indexOf相同,只不过遍历是从后向前开始
     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;
     }
6. clone方法,实际是返回了一个ArrayList实例的浅复制版本
     public Object clone() {
         try {
             // 调用Object类中的clone方法,此方法为natice方法
             ArrayList<?> v = (ArrayList<?>) super.clone();
             // 将数组及modCount赋值到v中
             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);
         }
     }
7. get,set方法
    public E get(int index) {
  			// 对index进行范围验证,检查index是否超出数组的下标范围
        rangeCheck(index);

        return elementData(index);
    }

		public E set(int index, E element) {
      	// 同样对index进行范围验证,检查index是否超出数组的下标范围
        rangeCheck(index);
				
      	// 取出index下标位置的旧值
        E oldValue = elementData(index);
      	// 将新值设置进index位置
        elementData[index] = element;
      	// 返回旧值
        return oldValue;
    }

		private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
8. 两个add方法
// 第一个add方法,直接添加元素,一般加在数组最后
     public boolean add(E e) {
         // 添加一个元素的操作,实际上数组要保证容量不少于当前size加1,如果不够则要扩容
         ensureCapacityInternal(size + 1);  // Increments modCount!!
         // 容量得到保证后,插入新值,并返回添加成功(true)
         elementData[size++] = e;
         return true;
     }
 ​
     private void ensureCapacityInternal(int minCapacity) {
         // 内部调用了ensureExplicitCapacity方法,见下面
         ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
     }
     
     // 上面已经介绍过,如果数组容量不够则要扩容
     private void ensureExplicitCapacity(int minCapacity) {
         modCount++;
 ​
         // overflow-conscious code
         if (minCapacity - elementData.length > 0)
             grow(minCapacity);
     }
 ​
 ​
     // 第二个add方法,指定新元素要插入的下标
     public void add(int index, E element) {
         // 保证index在数组有效范围内
         rangeCheckForAdd(index);
 ​
         // 添加一个元素的操作,实际上数组要保证容量不少于当前size加1,如果不够则要扩容
         ensureCapacityInternal(size + 1);  // Increments modCount!!
       
         // 调用系统层级的native复制方法,将原数组index位置及之后的所有元素,复制到数组的index + 1
         // 位置,需要执行复制的数组元素数量是size - index
         System.arraycopy(elementData, index, elementData, index + 1,
                          size - index);
         // 复制完成后,才开始将新值插入,保证不会覆盖到原有的index位置的元素
         elementData[index] = element;
         // 容量+1s
         size++;
     }
 ​
     private void rangeCheckForAdd(int index) {
         if (index > size || index < 0)
             throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
     }
9. remove方法
     // 通过下标删除元素
     public E remove(int index) {
         // 对index进行范围验证,检查index是否超出数组的下标范围
         rangeCheck(index);
 ​
         modCount++;
         E oldValue = elementData(index);
 ​
         // 删除元素时需要移动的元素个数
         int numMoved = size - index - 1;
         if (numMoved > 0)
             // 调用系统层级的native复制方法,将原数组index + 1位置及之后的所有元素,复制到数组的index位置,需要执行复制的数组元素数量是size - index - 1
             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;
     }
     
     // 此处逻辑与上面的remove的部分逻辑差不多,将index + 1及之后的元素移动到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
     }
10. clear方法
    public void clear() {
         modCount++;
 ​
         // 遍历所有位置将所有位置置空,让后面GC可以进行垃圾回收
         for (int i = 0; i < size; i++)
             elementData[i] = null;
             
         // size置0
         size = 0;
     }
11. addAll方法
     // 集合c添加到原数组的后面
     public boolean addAll(Collection<? extends E> c) {
         // 将集合c转为数组
         Object[] a = c.toArray();
         // 要添加的数组的长度
         int numNew = a.length;
         // 数组要保证容量不少于当前数组的size+要添加进入的数组的长度numNew,如果不够则要扩容
         ensureCapacityInternal(size + numNew);  // Increments modCount
         // 系统层级的native方法,将数组a复制到原数组的后面
         // a:源数组,0:源数组的开始位置,elementData:目标数组,size:目标数组的开始位置,numNew:需要进行复制的数组元素的长度
         System.arraycopy(a, 0, elementData, size, numNew);
         size += numNew;
         return numNew != 0;
     }
 ​
     // 集合c添加到源数组指定的index位置
     public boolean addAll(int index, Collection<? extends E> c) {
         // 保证index在数组有效范围内
         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;
     }
12. removeRange方法
    // 移除某个范围内的元素
    protected void removeRange(int fromIndex, int toIndex) {
        modCount++;
      	// 移除元素范围为[fromIndex, toIndex],因此要向前移动的元素个数为size - toIndex
        int numMoved = size - toIndex;
      	// 将toIndex位置之后的元素向前移动到toIndex处,完成remove操作
        System.arraycopy(elementData, toIndex, elementData, fromIndex,
                         numMoved);

        // 将后面没有用的位置置空,方便GC垃圾回收
        int newSize = size - (toIndex-fromIndex);
        for (int i = newSize; i < size; i++) {
            elementData[i] = null;
        }
        size = newSize;
    }
13. removeAll方法
    public boolean removeAll(Collection<?> c) {
         // 调用了Object.java里面的方法,判断集合是否为空,空的话判处空指针异常
         Objects.requireNonNull(c);
         return batchRemove(c, false);
     }
     
     // Object.java里面的方法
     public static <T> T requireNonNull(T obj) {
         if (obj == null)
             throw new NullPointerException();
         return obj;
     }
 ​
     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 {
             // c.contains()方法报异常,将剩下的元素赋值给elementData
             if (r != size) {
                 System.arraycopy(elementData, r,
                                  elementData, w,
                                  size - r);
                 w += size - r;
             }
             // 1. 如果w为0,实际上集合c的元素与elementData的元素完全相同,效果类似clear,全为null
             // 2. 如果w不为0且小于size,表示elementData含有部分/全部集合c的元素,因为上面try中
             // 的for循环已经将非相交元素前移,所以只需要将w位置之后的元素置空即可
             if (w != size) {
                 // clear to let GC do its work
                 for (int i = w; i < size; i++)
                     elementData[i] = null;
                 // 修改的次数
                 modCount += size - w;
                 // 最后elementData的大小
                 size = w;
                 // 表示完成了修改,返回true
                 modified = true;
             }
         }
         return modified;
     }
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值