ArrayList:源码解析

一、什么是ArrayList

      ArrayList是实现了List接口的一个集合

二、ArrayList底层是什么

      在对ArrayList的源码分析前,我们首先要了解一点就是ArrayList是什么。这里我们可以看一下ArrayList的属性

transient Object[] elementData;// transient: 该属性不会被序列化

我们看这个属性我们可以看到其实ArrayList的底层就是一个Object的数组。

三、ArrayList的源码解析

先从构造函数开始

	private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
	private static final Object[] EMPTY_ELEMENTDATA = {};
	// 这是ArrayList的无参构造
    public ArrayList() {
    	// 这里就是将空数组赋值给elementData
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    // 有参参数为一个整形
    public ArrayList(int initialCapacity) {
    	// 参数是否大于0
        if (initialCapacity > 0) {
        	// 参数大于0就为数组赋初始长度,也可以理解为集合大小
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
        	// 等于0就赋值为空数组
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
    // 这个用的很少
     public ArrayList(Collection<? extends E> c) {
     	// 将传入的集合转为数组
        elementData = c.toArray();
        // 看传入的集合是否为空集合也就是空数组
        if ((size = elementData.length) != 0) {
            // 不是这里就判断返回的是不是一个Object
            if (elementData.getClass() != Object[].class)
            	// 如果不是Object就用copyOf生成一个新的数组
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // 是就赋值为空数组
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

我们再看看常用的几个方法

	// 这个size指实际集合里有多少个数据
    private int size;
    // 获取ArrayList的当前有多少个数据
    public int size() {
        return size;
    }
    // 判断ArrayList集合里有没有数据
    public boolean isEmpty() {
        return size == 0;
    }
    // 获取该索引下的数据
    public E get(int index) {
    	// 这个就是个抛出异常的
        rangeCheck(index);
        return elementData(index);
    }
    // 修改当前索引的指并返回原先的指
    public E set(int index, E element) {
        rangeCheck(index);
		// 获取以前的指
        E oldValue = elementData(index);
        // 传递新指
        elementData[index] = element;
        return oldValue;
    }
    // 判断这个指是否在这个集合中
    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;
    }

四、ArrayList扩容源码分析

ArrayList的扩容首先要从添加开始说起:

	// 这是向集合中添加数据的方法
    public boolean add(E e) {
    	// ensureCapacityInternal()这个方法就是扩容的开始
        ensureCapacityInternal(size + 1);  
        // 这就是一个正常的数组赋值方法
        elementData[size++] = e;
        return true;
    }
	
    private void ensureCapacityInternal(int minCapacity) {
    	// 这个方法首先我们要看当前的数组是不是一个空数组
    	// 如果这不是一个空数组就不会执行判断体里的代码
    	// 就比如public ArrayList(int initialCapacity) 
    	// 这个有参构造创建的就不是一个空数组因为有初始长度了
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        	//如果是空数组使用Math.max比较传入的参数和默认的参数那个大
        	// DEFAULT_CAPACITY = 10,minCapacity=0
        	// 所以在初始化是一个空数组的时候默认的长度为10
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
		// 继续进行下一步分析
        ensureExplicitCapacity(minCapacity);
    }
    
    private void ensureExplicitCapacity(int minCapacity) {
    	// modCount修改次数
        modCount++;
		// 在这里进行判断你是不是需要进行扩容
		// minCapacity当前的集合大小
		// elementData.length初始的集合大小
		// 当你进行添加时发现当前的集合大小大于初始的集合大小
		// 那么就需要进行扩容
        if (minCapacity - elementData.length > 0)
        	// 扩容方法
            grow(minCapacity);
    }
    
    private void grow(int minCapacity) { 
    	/*
    		这里有两种说法:
    			第一种就是初始化时不设置长度
    			第二种就是初始化时设置初始长度
    	*/
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
         // 这个不进行说明,这个判断基本就是false
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // Arrays,copyOf(原始数组,新数组大小);
        // Arrays.copyOf方法是返回一个新的数组
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

先说第一种就是初始化时不设置长度:

  1. 初始化时没有设置长度所以就是一个空数组因此elementData.length就为0
  2. 初始长度为0导致扩容以后的长度还是为0,oldCapacity + (oldCapacity >> 1)通过这段代码我们可以得知扩容后的长度为扩容前的1.5倍 (>> 是位运算)
  3. newCapacity - minCapacity:在这里newCapacity = 0而minCapacity = 10,(为什么等于10前面说过了)所以就把minCapacity的值赋给了newCapacity:newCapacity = minCapacity;
  4. 最后通过Arrays.copyOf生成一个新的数组赋给了elementData

第二种初始化时设置长度

  1. 初始化时设置了长度因此elementData.length就是初始化的长度
  2. 因此后面的扩容也就是根据这个初始长度的1.5倍进行扩容
  3. 在这里newCapacity就不是0了是初始长度的1.5倍
  4. 后面都是一样的了

五、结语

其实通过源码的分析我们可以看出ArrayList集合的难度其实并不大,这里最主要的也就是ArrayList的扩容机制了,至于ArrayList什么时候扩容就是添加的实际数量大于默认或者初始的长度时,就会开始进行扩容。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值