ArrayList源码分析

概述:ArrayList是一个可以动态增长的索引序列,底层是数组。

源码分析:

	继承:
		AbstractList抽象类
		该类实现了List<E>接口一些通用方法,其子类可以得到这些通用方法,实现自己特有的方法,使得代码更简洁,减少重复代码。
	实现: 
		List接口,为了设计。
		RandomAccess接口,一个标记性接口,用来快速随机存取,提高普通for循环遍历的性能。
		Cloneable接口,一个克隆接口,实现它就可以使用object.clone()方法。
		Serializable接口,一个序列化接口,可以将类转化成字节流传输。
	特点: 
		1. 可以存放null值。
		2. 底层是一个elementData数组。
		3. grow()方法,ArrayList的核心方法,可以通过该方法进行扩容。
		4. 查询快,但是增删慢。

	属性:
	    	// 版本号
	    	private static final long serialVersionUID = 8683452581122892189L;
	    	// 缺省容量
	    	private static final int DEFAULT_CAPACITY = 10;
	    	// 空对象数组
		   	private static final Object[] EMPTY_ELEMENTDATA = {};
		   	// 缺省空对象数组
	    	private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
		   	// 元素数组
		   	transient Object[] elementData;
	    	// 实际元素大小,默认为0
	    	private int size;
	    	// 最大数组容量
	    	private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

构造方法:

作用:初始化一个默认10的容量(也可以自定义容量)的 elementData 数组。
重点:在构造方法中,只是定义容量大小,还没真正实现!(在grow方法实现)

1. 无参构造:

/**
 *   默认10的容量
 */
public ArrayList() {  //elementData其实就是底层数组
 	 //这里赋给一个空数组,容量在程序调用add()方法时,进行默认10的容量初始化。
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; 
}

2. 有参构造:

/**
 * 指定容量的初始化构造函数
 * 		initialCapacity:指定数组容量大小
 */
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);
    }
}
/**
 *  将指定集合添加到ArrayList集合中
 */
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方法:

1.boolean add(E e)   //添加元素,默认在末尾添加
2.void add(int index, E element) //在指定位置添加元素
3.boolean addAll(Collection<? extends E> c) //添加一个集合元素进本集合
4.boolean addAll(int index, Collection<? extends E> c)//添加一个集合元素进本集合的特定位置

1. add(E e)

/**
 * 在数组的末尾添加元素
 */
public boolean add(E e) {		
    ensureCapacityInternal(size + 1);   //判断加入元素后的总数量有没有超过数组容量
    elementData[size++] = e;		//添加元素e进数组,然后 size+1	
    return true;
}

下面是具体方法实现:

/**
 * 先判断底层数组是否为空数组,如果为空:minCapacity=10。如果不为空:minCapacity原样输出
 * 			minCapacity:数组的容量
 * 			elementData:数组
 */
private void ensureCapacityInternal(int minCapacity) {
	// 如果 elementData 是空数组,无参构造
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { 	
    	// 赋值,默认为容量为10,除非minCapacity更大。
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);  
    }

    ensureExplicitCapacity(minCapacity);
}
/**
 * 判断加入元素后的总数量有没有超过数组容量
 * 		
 * 			minCapacity:数组的容量
 * 			elementData:数组
 */
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;				//修改次数增加

   //一、elementDate 为空数组,minCapacity为10,10-0=10>0,进行第一次扩容,也即是数组容量初始化。
   //二、elementDate 不为空数组,判断此时的元素总数是否大于数组的容量。
    if (minCapacity - elementData.length > 0)  	
        grow(minCapacity);		//扩容
}

真正扩容和初始化数组容量的地方grow()方法。

/*
* 实现初始化容量和扩容 
* */
private void grow(int minCapacity) {       
	// oldCapacity 数组大小,如果是无参构造的空数组,大小为0,minCapacity为10
    int oldCapacity = elementData.length; 
    //扩容为原来oldCapacity 的1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);   
  
    if (newCapacity - minCapacity < 0)		//1.5倍不够,或者elementData为空数组(0-10=-10)
        newCapacity = minCapacity;				//要多少是什么(空数组:初始化容量为10)
   
    if (newCapacity - MAX_ARRAY_SIZE > 0)			//容量是否超过原本限制的最大容量MAX_ARRAY_SIZE
        newCapacity = hugeCapacity(minCapacity);			// 最多能给多少是多少

	// 方法参数:elementData:要赋值的数组,newCapacity:复制后的容量大小
    elementData = Arrays.copyOf(elementData, newCapacity);	//搬迁数组。
}
/*
* 当新扩容的容量(1.5倍)大于 MAX_ARRAY_SIZE时,新容量的取值。
* 		返回能够取的最大值
* */
private static int hugeCapacity(int minCapacity) {	
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
        
    // 将未扩容的容量与 MAX_ARRAY_SIZE 比较
    // MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;			
    return (minCapacity > MAX_ARRAY_SIZE) ?	
        Integer.MAX_VALUE :												
        MAX_ARRAY_SIZE;
}

2.add(int index, E element)

/*
*  在指定索引位置添加元素
* 			index:指定索引
* 			element:要添加的元素
* */
public void add(int index, E element) {
    rangeCheckForAdd(index);		//检查索引是否越界。

    ensureCapacityInternal(size + 1);  	//判断加入元素后的总数量有没有超过数组容量。
    System.arraycopy(elementData, index, elementData, index + 1, //将elementData在插入位置后的所有元素往后面移一位。
                     size - index);	// index为要移动前的起始位, index+1为移动后的起始位,size-index为移动数量。
    elementData[index] = element;	//添加元素。
    size++;	
}
/*
* 判断输入的 index 有没有越界
* */
private void rangeCheckForAdd(int index) {
	if (index > size || index < 0)		//判断index有没有越界。
	    throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

remove方法:

1. E remove(int index):删除并返回指定索引的值
2. boolean remove(Object o):删除该值
3. void clear():清理所有数据
4. boolean removeAll(Collection<?> c) :批量删除

1. E remove(int index);

/*
* 在指定位置移除元素
* */
public E remove(int index) {
    rangeCheck(index);		//判断是否越界

    modCount++;				//修改次数增加
    E oldValue = elementData(index);  	//取出该值

	// 删除一个索引的元素,后面的元素都要往前移一位。
    int numMoved = size - index - 1;	//numMoved为移动的元素数量
    if (numMoved > 0)	//(size-1) - index,判断 index在[0,size-1)内,如果不是最后一个元素,不用移动数组。
    	//元素移动
    	//elementData:选择数组。
    	//index+1:选择数组中要复制元素的起始位。
    	//elementData:目标数组。
    	//index:目标数组要存放加进来元素的起始位。
    	//numMoved:移动的元素数量。
        System.arraycopy(elementData, index+1, elementData, index, numMoved);	
        
    elementData[--size] = null; 		//清理原来 index=size-1的元素,size-1
    return oldValue;		
}

2. boolean remove(Object o);

/*
* 移除指定元素
* 		o:要删除的元素
* */
public boolean remove(Object o) {
	// 如果要删除空值:使用 == 比较内容,否则使用equals 
    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])) { //上面排除的null值,可以使用equals方法,防止空指针异常。
                fastRemove(index);
                return true;
            }
    }
    return false;
}

3. void clear():

public void clear() {		//所有值赋null,方便回收
    modCount++;
    // clear to let GC do its work
    for (int i = 0; i < size; i++)
        elementData[i] = null;
    size = 0;
}

4. boolean removeAll(Collection<?> c)

public boolean removeAll(Collection<?> c) {
    Objects.requireNonNull(c);
    return batchRemove(c, false);	// 调用batchRemove ,批量删除c与elementData共同的元素
}
public boolean retainAll(Collection<?> c) {	//也调用 batchRemove,求交集
    Objects.requireNonNull(c);
    return batchRemove(c, true);
}
//batchRemove
private boolean batchRemove(Collection<?> c, boolean complement) {  //complement : true:求交集 flase:删除
    final Object[] elementData = this.elementData;		
    int r = 0, w = 0;			//r控制循环,w为交集个数
    boolean modified = false;
    try {
        for (; r < size; r++)
            if (c.contains(elementData[r]) == complement)	//如果有交集,集合从index=0开始覆盖元素,添加与集合c有交集的元素
                elementData[w++] = elementData[r];		//如果是批量删除,集合从index=0开始覆盖元素,添加与集合c无交集的元素
    } finally {
        // Preserve behavioral compatibility with AbstractCollection,
        // even if c.contains() throws.
        if (r != size) {						//contains异常处理                           
            System.arraycopy(elementData, r,
                             elementData, w,
                             size - r);			
            w += size - r;
        }
        if (w != size) {
            // clear to let GC do its work
            for (int i = w; i < size; i++)
                elementData[i] = null;
            modCount += size - w;
            size = w;
            modified = true;
        }
    }
    return modified;
}

set方法

public E set(int index, E element) {  //在指定位置置换元素
    rangeCheck(index);			//判断 index 是否越界

    E oldValue = elementData(index);
    elementData[index] = element;	//替换元素
    return oldValue;
}

get方法

public E get(int index) {
    rangeCheck(index);

    return elementData(index);
}

总结:

一、初始化容量数组步骤:
	1. 构造方法定义容量大小,默认为10。
	2. add方法,确认容量的大小。
	3. grow方法,真正的实现容量。
二、扩容
	grow方法,正常情况下扩容为原来的1.5倍。
三、遍历
	由于ArrayList底层是数组,实现了 RandomAccess 接口的list,支持快速随机访问,优先选择普通 for 循环,其次 foreach。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值