ArrayList部分源码

目录

 

1.重要属性和构造

2.核心方法

add

remove

get

set

3.总结


1.重要属性和构造

ArrayList内部维护了一个数组,这个数组才是真正存储数据的容器

 transient Object[] elementData; 

当进行初始化new ArrayList<E>()时,会给elementData赋值一个空数组.

2.核心方法

add

 public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
 }

 里面就两句,第二句好理解,就是给elementData的尾巴添加一个元素.

第一句的作用则是检查扩容elementData,此时elementData.size = 0,深入查看一下.

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

还得深入一下,先看 calculateCapacity(空数组,1);

 private static int calculateCapacity(Object[] elementData, int minCapacity) {
      
        //DEFAULT_CAPACITY默认为10,
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        //如果不是空数组,则保持不变
        return minCapacity;
    }

 calculateCapacity方法目的是为了获得现在elementData最少要有的长度,否则盛不下那么元素。在add方法里面让现有的容量加一传参过来,在calculateCapacity方法里基本上直接返回minCapacity,当然这里面也做了小的调整,说如果数组是空的,也就是第一次添加的时候,就让最少要有长度设置为10

接下来看ensureExplicitCapacity方法

 private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        
        // overflow-conscious code
        
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

modCount实际上是指数组修改了多少次.这个这里没什么用.

下面两句意思是如果最少要有的长度比现在数组的长度还要大,也就是说现在这个elementData已经不能满足再添加一个元素了,容量不够了,就执行grow方法

接着看grow

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        //关键在这里 新数组的长度 = 旧长度 + 旧长度/2
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        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 
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

结论:

新建ArrayList后,默认elementData为空数组。

第一次添加元素,由于ensureExplicitCapacity方法里的10-0>0,则会执行grow方法,将elementData扩容为长度为10的数组,然后往里面添加元素

第二次添加元素,由于ensureExplicitCapacity方法里的2-10<0,则不走grow,直接往elementData里面添加元素。

第三次添加元素。。。

。。。

。。。

第十次添加元素,由于ensureExplicitCapacity方法里的11-10>0,则会执行grow方法,将elementData扩容为长度为10+5 = 15的数组,然后往里面添加元素

。。。

。。。

第十五次添加元素,由于ensureExplicitCapacity方法里的16-15>0,则会执行grow方法,将elementData扩容为长度为15+7 = 22的数组,然后往里面添加元素

。。。

。。。

以此类推,直到数组容量达到Int的最大值-8。

由此可见,ArrayList里add方法是要不定时的copy数组,相当消耗资源

remove

只看下面这个方法,根据索引删除

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

        modCount++;
        E oldValue = elementData(index);

        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

        return oldValue;
    }

其他都还好,如果你仔细揣摩的话,你就会了解,基本上所有的remove都会执行 System.arraycopy这个方法。本方法里这个arraycopy的意思是会将被删除的元素后面的所有元素都往前挪一位。效果相当于

for(){
    //如果是最后的元素,将元素置为null
    if (i == size){
        arr[i] = null;
        break;
    }
    //让元素往前挪一位
    if (i >= remove_index){
        arr[i] = arr[i+1];
    } 
}

其他的remove方法里面也这样System.arraycopy了。

结论:

ArrayList的remove方法均会操作“被删除索引”后的所有元素,非常消耗资源。

get

源码是这样的。非常简单。

E elementData(int index) {
        return (E) elementData[index];
}

结论:

Arraylist的get方法非常简单高效

set

 elementData[index] = element;

也基本上是这个操作。

结论:

ArrayList的set方法也是非常简单高效。

3.总结

ArrayList的get和set简单高效,add和remove非常耗资源,如果数组元素非常多的话,估计要炸。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值