ArrayList源码分析

1. ArrayList 是基数组结构的,需要连续的内存空间

  从构造函数可以看出,ArrayList内部用一个Object数组来保存数据。

  对于无参构造,ArrayList会创建一个大小为10的初始数组,第一次扩容时会创建默认大小数组。

 //无参构造函数,会构造一个空数组。第一次扩容时会创默认大小为10的数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    transient Object[] elementData;
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    private static final Object[] 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);
        }
    }

2.对于set和get方法ArrayList效率高,因为可以直接通过下标获取存储对象。

 //直接通过角标从数组中取出元素
    public E get(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        return (E) elementData[index];
    }
    //set 方法,直接通过index 拿到元素,进行修改就行。
    public E set(int index, E element) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

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

3.对于add方法,有两种情况:

       第一种是在尾部添加,不需要移动数组。

//Add 方法 ,在末端插入不需要
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

       第二种情况在指定位置添加或插入数据,则需要移动数组,效率比较低。如果插入在第0个位置则需要移动整个数组。

  public void add(int index, E element) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //被插入的元素在index。则移动的数组应该从index开始,向后移动一个,
		//移动的数组长度 size-index 从index开始的数组长度
        System.arraycopy(elementData, index, elementData, index + 1,
                size - index);
        elementData[index] = element;
        size++;
    }

在add时还需要考虑到数组的扩容问题,看ensureCapacityInternal方法

 private void ensureCapacityInternal(int minCapacity) {

        //通过无参构造创建为设置初始大小时,判断条件为true
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            //创建默认大小的数组,初始大小是10
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

 添加元素后,元素数量大于数组长度时,进行扩容grow,扩容大小是原数组的1.5倍

 private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        //新增后,数组容量已经大于了数组长度,则进行扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;

		//新的数组大小是原来的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);  
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //创建一个新长度的数组。
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

4.对应remove 删除元素操作。如果删除元素在最后一个,不需要移动数组,否则会将被删除元素之后的所有元素都要向前移动,效率比较低。

 //删除
    public E remove(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        modCount++;
        //取出被删除的元素
        E oldValue = (E) elementData[index];

        int numMoved = size - index - 1;
		//移动的数组长度,大于0才需要移动,小于0说明被删元素在最后一个
        if (numMoved > 0)
            //第一个参数是源数组,
            //第二个参数是移动的起始位置:index+1,从被删元素的后一个位置开始
            //第三个参数是目标数组
            //第四个参数是移动目标位置:index,被删除元素的位置
            //第五个参数是移动的数组长度 size - index - 1,从index+1位置开始之后的数组长度
            //最终会调用到native方法进行移动。
            System.arraycopy(elementData, index+1, elementData, index,numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

5.System.arraycopy方法参数分析。

 

        第二个参数 index+1是被移动数组的起始位置,即被删元素后一个位置

        第四个参数index,是被移动数组的起始位置,即被删元素的位置。

       最后一个参数size-index-1,被移动数组的长度,即被删元素后一个位置到元素末尾的长度

public static native void arraycopy(java.lang.Object src, int srcPos, java.lang.Object dest, int destPos, int length);

通过对源码的阅读,也许会对ArrayList有一个更加直接的了解。

    

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

niuyongzhi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值