ArrayList 源码机制解读 Java

ArrayList 源码机制解读 Java

ArrayList简介

集合中ArrayList是有序、可重复的(有序指的是元素插入的顺序是有序,可重复是指元素可以重复)

底层原理:
是通过数组实现的。但是它有自动的扩容机制。

特点:

  • 优点:

     		搜索速度快,因为可以通过下标直接定位到目标对象。
     		末端插入删除速度最快
    
  • 缺点:

     		对内存的要求高,因为添加删除需要移动元素位置。
    

底层源码分析

先了解一下,ArrayList的属性以及它的构造器:

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
	// DEFAULT_CAPACITY是默认的缺省数组长度,为10.
	// 先记住,如果我们new了一个空的ArrayList(没有传递参数), 那么它的默认长度就是10;
    private static final int DEFAULT_CAPACITY = 10;
    //声明的空数组,这个是有参构造器使用的
    private static final Object[] EMPTY_ELEMENTDATA = {};
    //声明的空数组,以供无参构造器使用
    private static final elementDataDEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    //存数据的缓冲数组,可以理解为就是我们在操作的数组(错了请大神指出)
    transient Object[] elementData;
    //数组的长度,也就是元素个数
    private int size;
}

构造器

//有参构造器
    public ArrayList(int initialCapacity) { //传入参数
        if (initialCapacity > 0) { //如果参数大于0,新建一个Object类型的数组并用指定的参数作为数组长度,之后赋值给属性中声明好的缓冲区数组elementData.
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {//如果传入的参数是0,就是建立一个长度为0的数组
            this.elementData = EMPTY_ELEMENTDATA; //使用EMPTY_ELEMENTDATA
        } else { //如果传入的是负数,抛出异常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
//无参构造器
 public ArrayList() { //将DEFAULTCAPACITY_EMPTY_ELEMENTDATA赋值给缓存数组
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
 
//将集合作为参数传入
 public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
  
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
 
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }
 

了解了,属性和构造器后,我们来了解ArrayList的扩容原理。
首先,我们创建新的ArrayListList list = new ArrayList();注意:此时默认调用的是无参构造器;
也就是说用的是DEFAULTCAPACITY_EMPTY_ELEMENTDATA 赋值给 elementData;

接下来让我们添加数据,list.add("yy"); 这个时候进入add方法;

	//add方法,添加参数中的对象,到数组中
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  //size = elementData.length 
        elementData[size++] = e;
        return true;
    }
   

这里我们看到add方法首先调用了ensureCapacityInternal();方法, 这个方法的作用是,要确保数组的容量要比当前的数组元素多一个,保证要添加的数据可以被添加到数组中。

让我们在看一下ensureCapacityInternal();的源代码:它的内部用调用的ensureExplicitCapacity()方法,而在这个方法内部用调用了calculateCapacity()方法; 可以说这是一个连环压栈的方法;

    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
  • 接着我们继续压栈,看看连环压栈的两个方法:首先调用的时calculateCapacity();方法我们将minCapacity的当前值也就是1传入,

  • 注意,在初次调用add方法时,ensureCapacityInternal(size + 1); 这个时候minCapacity = 1;因为size在此时为0,elementData.length在此时数组还是空的;判断,当前的elementDate == DEFAULTCAPACITY_EMPTY_ELEMENTDATA, 这里其实这句代码的意思就是,数组是不是刚刚初始化。 如果是返回 Math.max(DEFAULT_CAPACITY, minCapacity); 中较大的一个数。DEFAULT_CAPACITY= 10; 而minCapacit = 1; 所以返回10;

  • 随后将10作为参数传入到ensureExplicitCapacity();中,接着进入判断,minCapacity - elementData.length > 0,如果结果为true数组将要扩容,调用grow方法(), 当前minCapacit =1;elementData.length =0;返回true,这是我们就要进行数组的扩容;

 //计算数组的容量,注意在这里,因为时第一次掉调用minCpacity为1
 private static int calculateCapacity(Object[] elementData, int minCapacity) {
 		//判断数组是不是刚刚初始化,如果是返回10,不是返回size+1
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity); //返回10
        }
        return minCapacity;
    }
   
 //方法:判断是否要进行扩容 
 private void ensureExplicitCapacity(int minCapacity) {
        modCount++; //记录数组的修改次数
        //如果当前需要的最小容量大于数组长度,扩容
        if (minCapacity - elementData.length > 0) //minCapacity = 10
            grow(minCapacity);
    }

因为,当前数组要扩容,让我们看看扩容源码:

扩容,我们将当前的minCapacity作为参数传入,此时为10;

  • oldCapacity 为当前的数组长度
  • newCapacity是我们要扩容后的长度,注意这里用的是右移1位+oldCapacity,其实就是扩容为1.5倍. 这里用位操作符是因为速度更快;

接下来,判读新的容量是否小于当前需要的最小的容量。如果是的话,将新的容量设置为当前所需最小容量,也就是minCapacity的当前值10,所以这也就是无参构造器new ArrayList时,默认初始容量为10的原因;

	//数组扩容
    private void grow(int minCapacity) { //初次minCpacity为1
        int oldCapacity = elementData.length; //当前的数组长度
        int newCapacity = oldCapacity + (oldCapacity >> 1); //扩容为原来的1.5倍数
        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);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值