ArrayList源码解析--基于jdk1.8

文章目录

    • 结构
    • 类的属性
    • 构造函数
    • 常用方法
      • add方法
      • contains方法
      • get方法
      • remove方法
      • clear方法
      • set方法
      • toArray方法
    • 扩容机制!!!!
    • 总结

结构

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{....}

ArrayList类继承自AbstactList类,并实现了RandomAccess、Cloneable、java.io.Serializable接口,表明其支持随机访问,克隆和序列化。

类的属性

// 序列化id
private static final long serialVersionUID = 8683452581122892189L;

/**
* Default initial capacity. 默认的初始化容量
*/
private static final int DEFAULT_CAPACITY = 10;

/**
* Shared empty array instance used for empty instances.
* 指定该ArrayList容量为0时,返回该空数组。
*/
private static final Object[] EMPTY_ELEMENTDATA = {};

/**
* 当调用无参构造方法,返回的是该数组。刚创建一个ArrayList 时,其内数据量为0。
* 它与EMPTY_ELEMENTDATA的区别就是:该数组是默认返回的,而EMPTY_ELEMENTDATA是在用户指定容量为0时返回。
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

/**
* 保存添加到ArrayList中的元素。 
* ArrayList的容量就是该数组的长度。 
* 该值为DEFAULTCAPACITY_EMPTY_ELEMENTDATA 时,当第一次添加元素进入ArrayList中时,数组将扩容值DEFAULT_CAPACITY。 
* 被标记为transient,在对象被序列化的时候不会被序列化。
*/
transient Object[] elementData; // non-private to simplify nested class access

/**
* ArrayList的实际大小(数组包含的元素个数/实际数据的数量)默认为0
* @serial
*/
private int size;
  
/**
* 分派给arrays的最大容量
* 为什么要减去8呢?
* 因为某些VM会在数组中保留一些头字,尝试分配这个最大存储容量,可能会导致array容量大于VM的limit,最终导致OutOfMemoryError。
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//备注:MAX.VALUE为0x7fffffff,转换成十进制就是2147483647,也就是数组的最大长度是2147483639;

构造函数

/**
 * 有参构造器,参数为0时返回 EMPTY_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);
        }
    }

    /**
     * 无参构造器,返回 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 默认数组
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * @param c the collection whose elements are to be placed into this list
     * @throws NullPointerException if the specified collection is null
     */
    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;
        }
    }

常用方法

add方法

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // 扩容操作判断
        elementData[size++] = e;
        return true;
    }

    public void add(int index, E element) {
        rangeCheckForAdd(index);//检查index参数是否合法

        ensureCapacityInternal(size + 1);  //扩容操作判断
        //利用System.arraycopy实现将elementData数组index后的元素全部后一个位置。
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

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的数组的长度

contains方法

public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }
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;
    }

调用indexOf方法,如果放回结果大于0,即存在。

get方法

public E get(int index) {
        rangeCheck(index);//判断参数是否合法

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

由于ArrayList底层为Object数组,支持随机访问,所以可以直接访问元素,只需要加上强制类型转换即可。

remove方法

传入int

public E remove(int index) {
        rangeCheck(index);//检查参数

        modCount++;//修改次数加一
        E oldValue = elementData(index);

        int numMoved = size - index - 1;//index后面的元素个数
        //如果index不是最后一个,利用System.arraycopy删除。
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; //size-1

        return oldValue;
    }

传入object

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

底层操作和传入int类型一致。

clear方法

public void clear() {
        modCount++;

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

        size = 0;
    }

set方法

public E set(int index, E element) {
        rangeCheck(index);

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

toArray方法

public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }
public <T> T[] toArray(T[] a) {
        if (a.length < size)
            // Make a new array of a's runtime type, but my contents:
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
        System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }

扩容机制!!!!

扩容算法是基于方法grow()的,ArrayList的几个构造参数,决定了在初始状态下,容器的大小要么为0要么为自己设定的某个值。那么具体的扩容,就是在add()等方法进行的时候进行一次判定——如果有数组溢出的风险,那么就进行扩容。

扩容算法是基于方法grow()的,ArrayList的几个构造参数,决定了在初始状态下,容器的大小要么为0要么为自己设定的某个值。那么具体的扩容,就是在add()等方法进行的时候进行一次判定——如果有数组溢出的风险,那么就进行扩容。
扩容的时候先判定数组大小。数组是空或者小于10,那么在扩容的时候将数组直接分配大小到10。之后每次扩容,是变成原数组长度的1.5倍。但是有一个最大值——2^31-9,如果大于这个数会看当前数组大小到底是多少,如果小于该值,那么直接扩容到,反之,则扩容到最大值。

在调用add时触发扩容机制

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

ensureCapacityInternal

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

调用ensureExplicitCapacity

calculateCapacity

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

ensureExplicitCapacity

private void ensureExplicitCapacity(int minCapacity) {
        modCount++; //对List修改的次数,为父类AbstractList的属性

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

当最小最小容量大于elementData的长度时进行grow扩容操作。

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        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);
    }

扩容时,新容量为原容量的1.5倍。若新容量小于最小容量(即add后的有效元素个数)则把minCapacity赋值给容量。假如新容量大于MAX_ARRAY_SIZE 2^31-8,则调用hugeCapacity。最后用Arrays.copyOf进行数组扩容。

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

总结

一、elementData[] 数组为object类数组,用于存放ArrayList中的元素,且数组长度不随ArrayList元素的个数增加而随之增加,要根据扩容算法,进行扩容。
二、size为ArrayList中存放的元素个数。
三、扩容操作为add操作触发。
四、构造函数:

  1. 无参时,默认elementData数组为DEFAULTCAPACITY_EMPTY_ELEMENTDATA空数组;
  2. 有参数时,若为0,则elementData数组为EMPTY_ELEMENTDATA空数组;
  3. 若传入Collection时,将collection对象转换成数组,然后将数组的地址的赋给elementData。如果数组的实际大小等于0(c中没有元素),将空数组EMPTY_ELEMENTDATA赋值给elementData。如果size的值大于0,则执行Arrays.copy方法,把collection对象的内容(可以理解为深拷贝)copy到elementData中。

五、扩容机制:

  1. 当elementData为空时,第一次添加元素,直接将数据扩容为10
  2. 若添加后元素个数(即size+1,minCapacity)大于当前elementData的长度,进行grow扩容操作。
  3. grow规定新容量为原容量的1.5倍。若新容量小于size+1,则令新容量等于minCapacity,若新容量大于MAX_ARRAY_SIZE (即2^31-8):此时若minCapacity大于MAX_ARRAY_SIZE 则新容量为Integer.MAX_VALUE(即2^31),否则为MAX_ARRAY_SIZE 。

参考链接

https://blog.csdn.net/iwts_24/article/details/90211266
https://blog.csdn.net/u010250240/article/details/89762912

你可能感兴趣的:(ArrayList源码解析--基于jdk1.8)