ArrayList源码分析(扩容机制)

本文详细解析了ArrayList类的三种构造器功能,重点探讨了其扩容机制,包括无参构造时的初始容量策略和后续的动态扩容规则。同时提到ArrayList是非线程同步的特性。
摘要由CSDN通过智能技术生成

首先ArrayList有三个构造器

1,无参构造器ArrayList()
public ArrayList() {
     this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
2,有参构造器ArrayList(int var1) 这个构造器可以创建一个指定容量的ArrayList对象
public ArrayList(int var1) {
  if (var1 > 0) {
       this.elementData = new Object[var1];
  } else {
       if (var1 != 0) {
           throw new IllegalArgumentException("Illegal Capacity: " + var1);
       }

       this.elementData = EMPTY_ELEMENTDATA;
  }
}
3,有参构造器ArrayList(Collection<? extends E> var1)这个构造器可以将一个Collection集合转换为ArrayList
public ArrayList(Collection<? extends E> var1) {
    this.elementData = var1.toArray();
    if ((this.size = this.elementData.length) != 0) {
        if (this.elementData.getClass() != Object[].class) {     			      					this.elementData = Arrays.copyOf(this.elementData,this.size,
            Object[].class);             
         }
     } else {
         this.elementData = EMPTY_ELEMENTDATA;
     }

}

ArrayList扩容机制:

测试代码:

ArrayList arrayList = new ArrayList();

for (int i = 0; i <= 15; i++) {
    arrayList.add(i);
}

我们把断点下到add方法,进入add方法

public boolean add(E var1) {
    this.ensureCapacityInternal(this.size + 1);
    this.elementData[this.size++] = var1;
    return true;	
}

在调用add方法时,会发现在添加元素操作之前先会执行ensureCapacityInternal()这个方法。这个方法翻译过来就是确保内部容量,为什么要执行这个方法呢? 因为我们在添加时要先确定容量是否够用。这个方法也就是ArrayList扩容机制的核心方法,一旦确定好容量后面直接加元素即可。而我们元素的添加是添加在这个elementData数组中的,我们先进入ensureCapacityInternal()方法

private void ensureCapacityInternal(int var1) {
    if (this.elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        var1 = Math.max(10, var1);
     }

    this.ensureExplicitCapacity(var1);
}

当我们进入这个ensureCapacityInternal()方法内部看到了它的结构,var1这个参数就是我们当前arraylist数组的个数,因为当前添加了一个元素所以所以var1=this.size (这时size为0因为没元素)+ 1 =1。我们观察这个方法发现它首先进行一个判断,判断当数组中的元素数量是不是默认的容量0,但是由于我用的是无参构造器所以在无参构造器中已经进行了this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;赋值操作(DEFAULTCAPACITY_EMPTY_ELEMENTDATA默认为空数组),所以条件成立,这时它让 var1 = Math.max(10, var1);由于当前var1 = 1,所以经过这个操作直接将10赋给了var1。这也就引出了我们的第一个结论:在使用无参构造器时数组长度为0但是在第一次添加时会扩容成一个容量为10的elementData数组。其实这一步就是在判断这个arraylist对象是不是由无参构造器所创建的,如果是无参构造器创建的就直接创建容量为10的elementData数组,再执行ensureExplicitCapacity(var1);方法,如果不是就直接执行ensureExplicitCapacity(var1);方法。ensureExplicitCapacity这个方法翻译过来就是明确确定容量。我们进入这个方法

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

其中modCount是修改次数的意思,我们先略过。我们发现真正扩容的方法是grow(var1);而在执行之前它要先满足这个条件才可以,这个条件是指当前元素数量大于elementData数组的长度,数组不够用了所以需要扩容了,这时候才进行 真正的扩容操作grow(var1);我们直接进去看源码,如果够用就不用执行grow()方法扩容

private void grow(int var1) {
    int var2 = this.elementData.length;
    int var3 = var2 + (var2 >> 1);
    if (var3 - var1 < 0) {
        var3 = var1;
    }

    if (var3 - 2147483639 > 0) {
        var3 = hugeCapacity(var1);
    }

      this.elementData = Arrays.copyOf(this.elementData, var3);
}

首先,var2是当前数组的长度。var3 = var2 + (var2 >> 1);这个操作是指 var2 + var2/2也就是1.5倍的当前数组长度赋值给var3。下面又进行判断这个var3是不是比var1大,就是说我扩容1.5倍之后的容量是不是比当前数组内元素数量大,如果扩容1.5倍后还没有当前元素数量大那么我直接扩容到当前元素个数大小的容量。如果没有那我就直接扩容到1.5倍。在最后它调用了Arrays.copyOf()方法,再继续追进去会发现它调用的其实是System.arraycopy()方法。这样这个扩容操作就完成了。

结论:
  • ArrayList对象中维护了一个Object类型的数组transient Object[] elementData;。

  • 当我们使用无参构造器时,elementData一开始为0,当添加元素时才会先扩容到10,之后每次按照1.5倍进行扩容,如果扩容1.5倍还不够直接扩容到添加元素后的总元素数量大小。

  • 当我们使用ArrayList(int var1)构造器时,他会创建一个指定容量为var1的elementData数组。this.elementData = new Object[var1];之后的每次扩容也是按照1.5倍来进行扩容,如果扩容1.5倍还不够还是直接扩容到添加元素后的总元素数量大小。

  • ArrayList是线程不同步的,并没有synchronized修饰。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值