ArrayList源码个人解读

首先是ArrayList的一些属性:

 1 private static final long serialVersionUID = 8683452581122892189L;
 2 
 3     /**
 4      * Default initial capacity.
 5      */
 6     private static final int DEFAULT_CAPACITY = 10;
 7 
 8     /**
 9      * Shared empty array instance used for empty instances.
10      */
11     private static final Object[] EMPTY_ELEMENTDATA = {};
12 
13     /**
14      * Shared empty array instance used for default sized empty instances. We
15      * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
16      * first element is added.
17      */
18     private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
19 
20     /**
21      * The array buffer into which the elements of the ArrayList are stored.
22      * The capacity of the ArrayList is the length of this array buffer. Any
23      * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
24      * will be expanded to DEFAULT_CAPACITY when the first element is added.
25      */
26     transient Object[] elementData; // non-private to simplify nested class access
27 
28     /**
29      * The size of the ArrayList (the number of elements it contains).
30      *
31      * @serial
32      */
33     private int size;

接下来从列表的各个方法来讲述源码

首先是add()方法。

1  ArrayList list = new ArrayList();
2         for (int i = 1; i <= 10; i++) {
3             list.add(i);
4         }

使用断点进行阅读源码。此时调用的是无参构造

1     /**
2      * Constructs an empty list with an initial capacity of ten.
3      */
4     public ArrayList() {
5         this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
6     }

也就是说ArrayList底层其实是名为elementData的数组,并且如果是调用无参构造,那么默认就是空数组。当开始调用add()方法时,数组长度便会变为10;

1  public boolean add(E e) {
2         ensureCapacityInternal(size + 1);  // Increments modCount!!先确定list容量,如果不够,则加1;
3         elementData[size++] = e;//将元素放入数组中,然后size在加一;
4         return true;
5     }

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

  private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//此时会执行这一块,这一块也就只有这时候才会被执行到,到继续添加元素时就不会执行到这了
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
private void ensureExplicitCapacity(int minCapacity) {
        modCount++;//这是代表整体被修改的次数。

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)//此时minCapacity = 10,而elementData.length是0,所以代表着要扩容
            grow(minCapacity);
    }

grow(minCapacity)是扩容的实现函数

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;// 获取当前数组的容量
        int newCapacity = oldCapacity + (oldCapacity >> 1);// 扩容。新的容量=当前容量+当前容量/2.即将当前容量增加一半(当前容量增加1.5倍)。
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;//这里代表的是真正初始化elementData数组的长度为10.之后也就不会运行这里.只要当前数组的容量不为0,扩容后一定比elementData数组大。
        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); //新的容量大小已经确定好了,就copy数组,改变容量大小。 //copyof(原数组,新的数组长度)
    }

如果一开始初始化的时候,就初始了长度,也就是有参构造,那么就是

 ArrayList list = new ArrayList(8);//长度为8的列表
        for (int i = 1; i <= 10; i++) {
            list.add(i);
        }

此时调用有参构造

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

此时发现就是判断初始的长度

之后依旧执行

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

此时的elementData长度就是8。而minCapacity长度是1

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

此时直接返回minCapacity = 1.

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

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

而这时不需要扩容,因此返回就行。

然后是add(int index,E element())方法

 ArrayList list = new ArrayList();
        for (int i = 1; i <= 10; i++) {
            list.add(i);
        }
        for (int i = 11; i <=15 ; i++) {
            list.add(i);
        }
      //  list.add(3);
        list.add(12,"list");

断点进入

public void add(int index, E element) {
        rangeCheckForAdd(index);//越界检查

        ensureCapacityInternal(size + 1);  // Increments modCount!!//确认list容量,如果不够,容量加1。注意:只加1,保证资源不被浪费
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;//将当前元素插入到索引处。
        size++;//列表长度加1
    }

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

private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);//这句代码的意思就是相当于把当前索引的后面所有的值都往后复制一位。
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
代码解释:
  Object src : 原数组
int srcPos : 从元数据的起始位置开始
  Object dest : 目标数组
  int destPos : 目标数组的开始起始位置
  int length : 要copy的数组的长度
接下来就是remove(int index)方法。
  ArrayList list = new ArrayList();
        for (int i = 1; i <= 10; i++) {
            list.add(i);
        }
        for (int i = 11; i <=15 ; i++) {
            list.add(i);
        }
      //  list.add(3);
    //    list.add(12,"list");
     //   list.indexOf(3);
      //  list.remove(new Integer(3));
        list.remove(3);

断点进入

 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// size减一,然后将索引为size-1处的元素置为null。为了让GC起作用,必须显式的为最后一个位置赋null值

        return oldValue;//返回被删除的元素
    }

如果是删除指定元素,也就是删除对象

 ArrayList list = new ArrayList();
        for (int i = 1; i <= 10; i++) {
            list.add(i);
        }
        for (int i = 11; i <=15 ; i++) {
            list.add(i);
        }
        list.add(3);
    //    list.add(12,"list");
     //   list.indexOf(3);
        list.remove(new Integer(3));

断点进入

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

其实就是遍历elementData数组,当第一个出现指定的对象时,就执行fastRemove(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
    }

这其实就是将该对象所对应索引的处的元素删除。也就是通过索引来删除指定元素其实。

因此如果list中有多个3的时候,执行一次remove对象的操作,就先删除索引值最小的那个3.

接下来执行indexof和lastIndexof方法

 ArrayList list = new ArrayList();
        for (int i = 1; i <= 10; i++) {
            list.add(i);
        }
        for (int i = 11; i <=15 ; i++) {
            list.add(i);
        }
        list.add(3);
    //    list.add(12,"list");
        list.indexOf(3);

该方法的含义是返回第一个碰到3这个对象的索引值。

//返回此列表中指定元素的第一个出现项的索引,如果该列表不包含该元素,则返回-1。
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; } /** * Returns the index of the last occurrence of the specified element * in this list, or -1 if this list does not contain the element. * More formally, returns the highest index <tt>i</tt> such that * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>, * or -1 if there is no such index. */
 //返回此列表中指定元素的最后一次出现的索引,如果该列表不包含该元素,则返回-1。
    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;
    }

最后就是clear()方法

public void clear() {
        modCount++;

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

        size = 0;
    }

这里也就是体现了ArrayList可以存储null的特点。

总结:1.ArrayList可以存储null

2.ArrayList默认扩容是1.5倍。如果一开始就调用无参构造,那么默认从空集变为10,然后如果再扩容还是按照1.5倍扩容。

3.底层实际是一个elementData的数组。,查询较快,但是插入删除较慢。

4.并且ArrayList是非线程安全的。

posted @ 2021-03-29 16:42  =凌晨=  阅读(72)  评论(0编辑  收藏  举报