ArrayList源码学习

119 阅读4分钟

transient

java 的transient关键字为我们提供了便利,你只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。

ArrayList主要的几个成员变量

      private static final int DEFAULT_CAPACITY = 10; //默认的数组的大小
      private static final Object[] EMPTY_ELEMENTDATA = {};//默认的数组
      private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//默认的数组,没什么不同,只是在不同的构造函数调用

主要的构造函数

     public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
  • 在这个构造函数里面只是初始化了一个空的数据elementData

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

    }

  • 这个构造函数初始化一个我们指定大小的Object数组.

查看add方法

    public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
     //将添加的元素设置到数组的末尾
    elementData[size++] = e;
    return true;
  }
  • add方法里面第一件事是来设置数组大小
  • 第二来给数组设置值

ensureCapacityInternal

   private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {  //这个判断主要是给集合添加第一个元素的时候,来指定默认的数组大小
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    //主要的扩展数组大小的方法
    ensureExplicitCapacity(minCapacity);
}

ensureExplicitCapacity

    private void ensureExplicitCapacity(int minCapacity) {
    modCount++;  //这个属性主要是用来记录集合变化的次数
    // overflow-conscious code
    //判断最小容量与数组的长度,当最小容量大于数组的长度的时候,说明数组的内存太小需要扩展
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

grow扩展最小的容量

         private void grow(int minCapacity) {
    // overflow-conscious code
      //当第一次往集合添加元素时候
     //elementData数组是一个空的数组,oldCapacity=0;
    int oldCapacity = elementData.length;
      //新的数组空间 newCapacity=oldCapacity+(oldCapacity >> 1)=0+0=0;第一次添加的时候,新的数组空间的大小也是0
    int newCapacity = oldCapacity + (oldCapacity >> 1);
     //第一次0-10=-10成立
    if (newCapacity - minCapacity < 0)
     //将新的数组空间设置成minCapacity=10;
        newCapacity = minCapacity;
        //判断数组最大时,数组空间的大小
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    //重新创建一个长度为newCapacity的数组,将elementData数据复制到新的数组中,并且将这个新的数组赋值给elementData
    elementData = Arrays.copyOf(elementData, newCapacity);
}

> 在网上看到oldCapacity >> 1就是oldCapacity/2   oldCapacity << 1 就是 oldCapacity*2 ,还需要学习一下位运算原理

remove方法

    public E remove(int index) {
     //判断移除的index大于,等于数组长度时候,就会报IndexOutOfBoundsException
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
   //用来统计数据变化的次数,可以不用管它
    modCount++;
      //获取到要移除的的元素
    E oldValue = (E) elementData[index];

    int numMoved = size - index - 1;
       //判断移除的元素是不是最后一个元素,要是不是最后一个元素就不需要在copy一个新的数组出来
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
   //返回删除的元素
    elementData[--size] = null; // clear to let GC do its work
    //返回删除的元素
    return oldValue;
}



System提供了一个静态方法arraycopy(),我们可以使用它来实现数组之间的复制。其函数原型是:
public static void (Object src,
                             int srcPos,
                             Object dest,
                             int destPos,
                             int length)

src:源数组; srcPos:源数组要复制的起始位置; dest:目的数组; destPos:目的数组放置的起始位置; length:复制的长度。 注意:src and dest都必须是同类型或者可以进行转换类型的数组.

set方法

public E set(int index, E element) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    //获取index上的旧的元素
    E oldValue = (E) elementData[index];
    //设置新的元素
    elementData[index] = element;
    return oldValue;
}

获取一个值lastIndex

    public int lastIndexOf(Object o) {
    if (o == null) {//判断o
        for (int i = size-1; i >= 0; i--)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = size-1; i >= 0; i--)
            //将数组从末尾开始循环遍历,找到相等的值,返回值的下标index
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}

重载的add方法add(int index, E element)

      public void add(int index, E element) {
    if (index > size || index < 0) //这里判断位置是否IndexOutOfBoundsException
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    //这里也是设置数组的大小
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    //主要是这里,复制一个新的数组
    //假如以前的数组是 [1,2,3]这个样子,我们需要再Index=2的位置添加一个元素4
    //那么使用arraycopy复制后的数组会变成[1,2,,3]
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    //设置数组指定位置指定的element,新的数组就会变成[1,2,4,3]
    elementData[index] = element;
    //设置数组的长度增加1
    size++;
}
  • 顺序存储线性表