ArrayList扩容机制

20 篇文章 0 订阅
6 篇文章 0 订阅

前言

ArrayList基本相关

ArrayList的构造方法

无参构造

根据传入的数值大小,创建指定长度的数组

ArrayList自动扩充

ArrayList的特点

前言

        继Springsecurity后,框架的学习暂时告一段落,现在回过头来学习Java的一些源码有些许感悟。从学习编程语言开始,我们都是先学习了数组,之后才是集合。众所周知,数组在定义的时候,一般都需要定义数组的空间大小,而ArrayList集合却并不需要。之前也浑然不在意,直到后来听别人问我,为什么集合在引用的时候可以不去传入空间大小?我无言以对,那时候才知道了ArrayList的自动扩充这个概念。

ArrayList基本相关

        提到ArrayList的自动扩充原理,不得不提的就是ArrayList中的构造方法与add()方法。首先我们先来看看ArrayList中定义的一些参数变量以及其中的两个构造方法。

// 默认的容量大小(常量)
private static final int DEFAULT_CAPACITY = 10;

// 定义的空数组(final修饰,大小固定为0)
private static final Object[] EMPTY_ELEMENTDATA = {};

// 定义的默认空容量的数组(final修饰,大小固定为0)
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

// 定义的不可被序列化的数组,实际存储元素的数组
transient Object[] elementData; 

// 数组中元素的个数
private int size;

        这些参数在刚开始看源码的时候显得有点折磨,完全看不懂,也是在之后慢慢看,一个个对应这找的时候才发现他们各自有各自的作用。显而易见,ArrayList也是由数组实现的,这也是它被称为动态数组的原因吧。

ArrayList的构造方法

ArrayList有三种构造方法,这里主要浅谈前两个:

  • 无参的构造方法
  • 根据传入的数值大小,创建指定长度的数组
  • 通过传入Collection元素列表进行生成

无参构造

// 无参的构造方法
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

        显而易见,当我们引用无参构造的时候,空数组elementData会被赋给集合,此时集合内的元素为0长度也为0。

根据传入的数值大小,创建指定长度的数组

/ 传容量的构造方法
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);
    }
}

        当initialCapacity > 0时,会在堆上new一个大小为initialCapacity的数组,然后将其引用赋给elementData,此时ArrayList的容量为initialCapacity,元素个数size为默认值0。

        当initialCapacity = 0时,elementData被赋予了默认空数组,因为其被final修饰了,所以此时ArrayList的容量为0,元素个数size为默认值0。

        当initialCapacity < 0时,会抛出异常。

ArrayList自动扩充

        对于自动扩充,必然与add()方法脱不开干系,ArrayList有两个add方法,其中一个跟自动扩充紧密联系,那么就直接进入正题。

  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++;
    }

        从这里还是看不出来什么的,首先rangeCheckForAdd(index)方法首先判断元素个数是否等于数组容量。

 private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

        之后就是ensureCapacityInternal()方法了,主要自动扩容也体现在这里面。

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

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

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

        可以看到,当当前数组是满的,这时候就需要扩容了,而源码中调用了grow()方法,那让我们一起来看看grow()方法。

 private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        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 = Arrays.copyOf(elementData, newCapacity);
    }

        oldCapacity用来记录旧数组的容量,而这个newCapacity就是扩充后的容量了,相信大家都知道,ArrayList会自动扩充为原容量的1.5倍,而oldCapacity >> 1 也就是将oldCapacity右移一位,也就是缩小到原来的二分之一,这样就是1.5倍了。

        后面的第一个if语句,用来检查新容量的大小是否小于最小需要容量,如果小于那就将最小容量作为数组的新容量。第二个if表示当新容量大于MAX_ARRAY_SIZE,使用hugeCapacity函数比较。最后将原数组中的元素拷贝到扩容后的数组中。

        因为对源码的理解还不够,结合者查资料与debug,目前只能总结到这个地步了,以后还会继续深入研究。

ArrayList的特点

  • ArrayList的底层数据结构是数组,所以查找遍历快,增删慢。因为实现了RandomAccess接口所以可以随机访问,也就是根据索引去获取元素。
  • ArrayList可随着元素的增长而自动扩容,正常扩容的话,每次扩容到原来的1.5倍。
  • ArrayList的线程是不安全的。
  • ArrayList实现了Serializable接口,所以ArrayList可以被序列化

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值