浅析ArrayList源码(二)————add方法分析以及扩容原理

前言

为什么要使用集合呢?这个问题想必对很多刚接触集合的人来讲,是个疑问。举个简单的例子,你申明一个整形数组,我现在要10个空间,int array[] = new int[10],很简单呀,再举个简单的例子,想申请一个整形数组,这个时候怎么解决?Java不像c++,可以灵活的malloc,如果加入malloc这种,那么Java不就和c++一样,存在着指针等问题的烦恼了,为了解决这种类似的现象,Java决定采用集合的面向对象的思想。

add方法:

要想使用ArrayList,那么第一步肯定就是得知道如何添加元素进入数组。

第一种:在末尾添加元素
    public boolean add(E e) {
        ensureCapacityInternal(size + 1); 
        elementData[size++] = e;
        return true;
    }

在分析这个之前,还记得上一篇博客中所说的默认初始容量为10么,继续分析:
首先,返回值是个boolean类型,add需要传入参数,当然是需要添加的元素,首先执行了ensureCapacityInternal(size + 1)。

分析ensureCapacityInternal(int minCapacity):

    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        ensureExplicitCapacity(minCapacity);
    }

传入了一个整形的参数,在这个if判断这里,清楚的回顾之前的构造方法,相等的时候便是使用默认构造函数申明的数组(如果没有想明白,请自行在回顾构造方法)。如果是默认构造方法,将minCapacity赋值为默认值与传入参数两者较大的值,然后执行ensureExplicitCapacity(minCapacity)方法。
分析ensureExplicitCapacity(minCapacity):

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

传入参数为前面得到的minCapacity,然后操作数加一,因为存在多线程访问的可能性,这个操作数modCount就是这个作用,每次进行操作便进行记录。如果这个值大于当前的数组长度,执行grow(minCapacity);
分析:grow(minCapacity)

    private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

先记录原本的数组长度,newCapacity为原本长度加上原本长度的右移一位,,右移一位相当于除以2(这个是扩容的方法)。当newCapacity小于传入的长度,newCapacity就等于传入的这个,当这个newCapacity大于最大数组长度的时候,执行hugeCapacity(minCapacity)。

最大长度定义:

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

分析:hugeCapacity(minCapacity)

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

当入参小于0,抛出异常OutOfMemoryError,反之,判断如果大于最大长度,返回整型的最大长度,反之返回最大的长度。注意:这两个长度不同,一个是整型的最大长度,一个是自己定义的最大长度。

返回上一层,elementData = Arrays.copyOf(elementData, newCapacity);,这个有必要解释一下,这里仅仅是将空间进行扩大,并没有进行赋值。然后返回到最初的add方法了,中间部分基本已经全部解释清楚了。

最初的add方法的下一步为赋值,将新的元素赋值到末尾,一切都正确,返回true。

第二种,在指定位置添加元素
    public void add(int index, E element) {
        rangeCheckForAdd(index);
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

首先执行先判断这个index是否合法,是否超过最大的size。该方法过于简单,拒绝分析。接下来执行ensureCapacityInternal(size + 1); 分析过了,作用有两个,一扩容,第二增加modCount。然后执行本地方法拷贝,index之前的不变,index位置往后元素全部往后移动一位,赋值。

总结

总的来说,也是一般写方法的常规思想,首先判断空间够不够,如果空间不够如何扩容,扩容的方法为,本身加上本身除以二,记录对数组的操作,防止多线程出现多次操作导致访问不同。然后就是扩大的空间是否超过了最大的空间,最大的空间为最大的整型值减去8,然后就基本判断完了,该插入的插入即可。总的来说,还是较为简单的。

积土成山,风雨兴焉!!!

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值