Arraylist源码解读

Arraylist

在这里插入图片描述

成员变量

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

/**
 * Shared empty array instance used for empty instances.
 * 用于空实例的共享空数组实例。
 */
private static final Object[] EMPTY_ELEMENTDATA = {};

/**
 * 用于默认大小的空实例的共享空数组实例。我们将其与EMPTY_ELEMENTDATA区分开来,
 * 以知道在添加第一个元素时要扩容多少。
 */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

/**
 * 存储ArrayList元素的数组缓冲区。ArrayList的容量是此数组缓冲区的长度。
 * 任何空的ArrayList(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
 * 将在添加第一个元素时扩展到默认容量。
 */ 
transient Object[] elementData; // non-private to simplify nested class access

/**
 * ArrayList的大小(它包含的元素数)。
 */
private int size;

构造方法

1、带参构造,即List list = new ArrayList(1);

/**
 * Constructs an empty list with the specified initial capacity.
 * 构造具有指定初始容量的空列表。
 * @param  initialCapacity  the initial capacity of the list 初始大小
 * @throws IllegalArgumentException if the specified initial capacity
 *         is negative
 * 如果initialCapacity为负数,报错IllegalArgumentException
 */
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {//如果指定初始大小大于0,elementData为指定大小的数组
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {//如果指定初始大小等于0,elementData为空数组实例
        this.elementData = EMPTY_ELEMENTDATA;
    } else {//如果为负数,报错
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

2、无参构造,即List list = new ArrayList();

/**
 * Constructs an empty list with an initial capacity of ten.
 * 构造初始容量为10的空列表
 * 源码注释是这么说,但是new的时候容量还是0,在add的时候创建容量为10的列表
 */
public ArrayList() {
	this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

3、参数为集合的构造

/**
 * 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) {
    //toArray()返回一个新的数组,包含c所有元素,顺序为参数原始顺序
    elementData = c.toArray();
    //如果size不为0
    if ((size = elementData.length) != 0) {
    	// c.toArray might (incorrectly) not return Object[] (see 6260652)
        // c.toArray可能(错误地)不返回Object[](参见6260652)
        if (elementData.getClass() != Object[].class)
            //转成Object[]
        	elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // replace with empty array.
        //size为0,则elementData为空数组实例
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

/**
 * Returns an array containing all of the elements in this list
 * in proper sequence (from first to last element).
 *
 * <p>The returned array will be "safe" in that no references to it are
 * maintained by this list.  (In other words, this method must allocate
 * a new array).  The caller is thus free to modify the returned array.
 *
 * <p>This method acts as bridge between array-based and collection-based
 * APIs.
 *
 * @return an array containing all of the elements in this list in
 *         proper sequence
 */
public Object[] toArray() {
    return Arrays.copyOf(elementData, size);
}

public static <T> T[] copyOf(T[] original, int newLength) {
    return (T[]) copyOf(original, newLength, original.getClass());
}

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    @SuppressWarnings("unchecked")
    T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new Object[newLength]
        : (T[]) Array.newInstance(newType.getComponentType(), newLength);
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}

方法

add(E e)

 /**
  * Appends the specified element to the end of this list.
  * 将指定的元素追加到此列表的末尾。
  * @param e element to be appended to this list.要附加到此列表的元素
  * @return <tt>true</tt> (as specified by {@link Collection#add})
  */
 public boolean add(E e) {
 	ensureCapacityInternal(size + 1);  // Increments modCount!!
 	elementData[size++] = e;
 	return true;
 }

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    //这里主要是判断是否是以无参构造创建的list,无参构造创建的list默认大小10,拿默认大小跟所需最小容量比较,取大值
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

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

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;//记录操作次数

    // overflow-conscious code
    //如果所需的最小容量比数组长度要大,则需要进行扩容
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

/**
 * 要分配的数组的最大大小。一些vm在数组中保留一些头字。尝试分配较大的数组可能会导致
 * OutOfMemoryError:请求的数组大小超过了虚拟机限制
 */
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

/**
 * Increases the capacity to ensure that it can hold at least the
 * number of elements specified by the minimum capacity argument.
 * 增加容量以确保它至少可以容纳最小容量参数指定的元素数。
 * @param minCapacity the desired minimum capacity 所需的最小容量
 */
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;//原数组大小
    int newCapacity = oldCapacity + (oldCapacity >> 1);//扩容1.5倍后的容量
    //如果扩容后的大小比所需最小容量(minCapacity)要小,list新的大小等于所需最小容量(minCapacity)
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    //如果扩容后的大小比MAX_ARRAY_SIZE还要大,那么扩容后的大小为Integer.MAX_VALUE
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    //扩容以后要进行全量copy
    elementData = Arrays.copyOf(elementData, newCapacity);
}

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

add(int index, E element)

/**
 * Inserts the specified element at the specified position in this
 * list. Shifts the element currently at that position (if any) and
 * any subsequent elements to the right (adds one to their indices).
 * 在list指定位置插入指定元素,将当前位于该位置的元素和任何后续元素向右移动(如果有的话)
 *
 * @param index index at which the specified element is to be inserted 要插入指定元素的索引
 * @param element element to be inserted 要插入的元素
 * @throws IndexOutOfBoundsException {@inheritDoc}
 */
public void add(int index, E element) {
    rangeCheckForAdd(index); //检查索引

    //这里跟上面add方法一样,判断是否需要扩容
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    //copy,从elementData索引为index开始copy,copy到目标数组elementData,目标数组索引为index + 1开始,要copy的元素个数为size - index
    System.arraycopy(elementData, index, elementData, index + 1,
    			size - index);
    //指定索引位置元素为element
    elementData[index] = element;
    //list大小+1,所包含的元素数
    size++;
}

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

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
    //copy,源数组a,从0开始copy,目标数组elementData,从size开始,即最后面,copy大小为传入数组大小
    System.arraycopy(a, 0, elementData, size, numNew);
    //更新size
    size += numNew;
    return numNew != 0;
}

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;
    //如果index在数组中间,先将index后面的元素copy到index + numNew的位置
    if (numMoved > 0)
    System.arraycopy(elementData, index, elementData, index + numNew,
    numMoved);

    //copy,源数组a,从0开始copy,目标数组elementData,从index开始,copy大小为传入数组大小
    System.arraycopy(a, 0, elementData, index, numNew);
    //更新size
    size += numNew;
    return numNew != 0;
}

remove(int index)

public E remove(int index) {
    rangeCheck(index);//检查索引

    modCount++;//记录操作次数
    E oldValue = elementData(index); //得到指定索引的值

    int numMoved = size - index - 1;
    //如果index在数组中间,copy,源数组elementData,从index+1开始copy,目标数组elementData,从index开始,copy大小为numMoved
    if (numMoved > 0)
    System.arraycopy(elementData, index+1, elementData, index,
    numMoved);
    //往前移一位后最后一个置为null
    elementData[--size] = null; // clear to let GC do its work

    return oldValue;
}

@SuppressWarnings("unchecked")
E elementData(int index) {
	return (E) elementData[index];
}

remove(Object o)

//这个就很容易看懂了,如果o为null,循环拿到索引,不为空也是循环拿到索引,fastRemove里逻辑跟remove里一样了
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
}

removeAll(Collection<?> c)

public boolean removeAll(Collection<?> c) {
    Objects.requireNonNull(c); //判断c是否为空,为空报空指针
    return batchRemove(c, false);
}

private boolean batchRemove(Collection<?> c, boolean complement) {
    final Object[] elementData = this.elementData; //拿到数组
    int r = 0, w = 0;
    boolean modified = false;
    try {
        //如果contains不报错,正常执行完循环,这步操作将把需要移除的数据都替换掉,不需要移除的数据前移。
        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作用主要是防止contains报错,没有执行完循环 r!=size
        if (r != size) {
            //这一步copy为,从源数组elementData索引为r开始copy(即索引r以及之后的元素还没有执行过替换,因为到r contains报错了),copy到目标数组elementData索引为w的后面(即w之前的已经执行过替换了)
            System.arraycopy(elementData, r,
                             elementData, w,
                             size - r);
            w += size - r;
        }
        if (w != size) {
            // clear to let GC do its work
            //最后将前移后,索引在w及后面的元素置null,也就是元素前移后,后面空出来的位置
            for (int i = w; i < size; i++)
                elementData[i] = null;
            //修改次数为size减去新数组大小
            modCount += size - w;
            //更新数组大小
            size = w;
            //操作成功,true
            modified = true;
        }
    }
    return modified;
}
说明:
size=8
原数组:["张三:0","李四:1","王五:2","赵六:3","麻子:4","小明:5","小红:6","露西:7"] elementData
需要移除数组:["麻子","露西"] c

说明:w++ 先用再加,即第一次循环w=0,elementData[w++]意思为:elementData[0]然后再w+1
    
                                    
循环次数 r值	c.contains值 	if判断值 w值循环前 elementData[w++] = elementData[r] w值循环后	
1       0   false		 	true	0	     elementData[0]=elementData[0]		1
2		1   false		 	true	1     	 elementData[1]=elementData[1]		2
3		2   false		 	true	2    	 elementData[2]=elementData[2]		3
4		3   false		 	true	3    	 elementData[3]=elementData[3]		4
5		4   true         	false    
6		5	false		 	true   	4	   	 elementData[4]=elementData[5]		5
7		6	false		 	true   	5	   	 elementData[5]=elementData[6]		6
8		7	true		 	false
    	8
r=8,w6

循环执行完之后,再对比一下两个数组的数据,
替换后数组:
["张三","李四","王五","赵六","小明","小红","小红","露西"]
原始数组:
["张三","李四","王五","赵六","麻子","小明","小红","露西"]
可以看出,将需要移除的元素替换后,后面的元素向前移了一位,
所以后面finally里的循环就是将小红,露西置空,成功remove。

另外一种情况,如果第六次循环contains报错,执行完第一个if后数组为:
                                                   5
第六次替换报错的数组:["张三","李四","王五","赵六","小明","小明","小红","露西"]
未遍历到的:"小明","小红","露西"
已遍历的:"张三","李四","王五","赵六"

执行完第一个if后:["张三","李四","王五","赵六","小明","小红","露西",null]
这样就确保了异常抛出前的部分可以完成期望的操作,而未被遍历的部分接到已经遍历过的后面
此时 r=5 w=4
    w += size - r=7
        

clear()

//这个不说了,自己看
public void clear() {
        modCount++;

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

        size = 0;
    }

retainAll(Collection<?> c)

# 跟removeAll的区别就在于第二个参数是true了,remove是删除指定的集合,retainAll就是删除不在指定集合中的元素
public boolean retainAll(Collection<?> c) {
    Objects.requireNonNull(c);
    return batchRemove(c, true);
}

set(int index, E element)

public E set(int index, E element) {
    rangeCheck(index);//检查索引

    E oldValue = elementData(index);//拿到旧值
    elementData[index] = element;//将指定下标index的值替换为element
    return oldValue;//返回旧值
}

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

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

扩容后的元素copy

/** 从指定的源数组(从指定位置开始)将数组复制到目标数组的指定位置。包含起始位
 * @param      src      the source array.源数组
 * @param      srcPos   starting position in the source array.源数组中的起始位置。
 * @param      dest     the destination array.目标数组。
 * @param      destPos  starting position in the destination data.目标数据中的起始位置。
 * @param      length   the number of array elements to be copied.要复制的数组元素数。
 */
System.arraycopy(Object src, int  srcPos, 
				  Object dest, int destPos, int length);

ArrayList和Vector的区别

Vector,它是ArrayList的线程安全版本,其实现90%和ArrayList都完全一样,区别在于:

1、Vector是线程安全的,ArrayList是线程非安全的

2、Vector可以指定增长因子,如果该增长因子指定了,那么扩容的时候会每次新的数组大小会在原数组的大小基础上加上增长因子;如果不指定增长因子,那么就给原数组大小*2,源代码是这样的:

int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                     capacityIncrement : oldCapacity);

总结

优缺点

  1. ArrayList底层以数组实现,是一种随机访问模式,再加上它实现了RandomAccess接口,因此查找也就是get的时候非常快。
  2. ArrayList在顺序添加一个元素的时候非常方便,只是往数组里面添加了一个元素而已。
  3. 删除元素的时候,涉及到一次元素复制,如果要复制的元素很多,那么就会比较耗费性能
  4. 插入元素的时候,涉及到一次元素复制,如果要复制的元素很多,那么就会比较耗费性能

特点:允许元素为null,允许重复元素,有序,非线程安全。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值