ArrayList集合默认长度


默认长度为10。但是ArrayList的默认长度是有jdk版本差异的,在jdk8版本之前默认长度是10。而在jdk8版本的时候对ArrayList数组的默认长度进行了优化,将原来的默认长度10,改为了初始长度为0。当我们在首次添加元素,需要分配数组空间时,jdk自动帮我们进行了扩容操作,将初始数组长度扩容成了10。这样做有效地降低了无用内存的占用,它利用了数组扩容的特性来完成集合的这些功能,这也就是ArrayList集合查询快、增删慢的原因


ArrayList中的Add方法


1.在集合最后一位添加一个元素,长度不够则进行扩容,添加成功返回true

public boolean add(E e) {
        //确保数组长度,不够则扩展
        ensureCapacityInternal(size + 1); 
        //添加元素
        elementData[size++] = e;
        return true;
    }

2.在指定索引位置添加元素,若索引越界则抛出异常

public void add(int index, E element) {
        //检查插入索引位置,超出范围,抛出异常,跟List长度作比较
        rangeCheckForAdd(index);
        //根据情况扩展数组长度,如果扩展数组长度,进行一次数组的复制
        ensureCapacityInternal(size + 1);  
        //赋值新数组
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        //添加元素
        elementData[index] = element;
        //list长度加1
        size++;
    }

private void rangeCheckForAdd(int index) {
         //如果索引大于List长度或者小于0,抛出异常
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

ArrayList的扩容机制

ArrayList是List接口的实现类,它是支持根据需要而动态增长的数组。java中标准数组是定长的,在数组被创建之后,它们不能被加长或缩短。这就意味着在创建数组时需要知道数组的所需长度,但有时我们需要动态程序中获取数组长度。ArrayList就是为此而生的。


以add方法为例,下面是上述add方法的源码:

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

根据意思可以看出ensureCapacityInternal()是用来扩容的,形参为最小扩容量,进入此方法后:

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

通过方法calculateCapacity(elementData, minCapacity)获取:

 private static int calculateCapacity(Object[] elementData, int minCapacity) {
        //如果传入的是个空数组则最小容量取默认容量与minCapacity之间的最大值
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

ensureExplicitCapacity方法可以判断是否需要扩容:

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

          // 如果最小需要空间比elementData的内存空间要大,则需要扩容
          if (minCapacity - elementData.length > 0)
              //扩容
              grow(minCapacity);
      }

下面是ArrayList扩容的关键方法grow():

private void grow(int minCapacity) {
          // 获取到ArrayList中elementData数组的内存空间长度
          int oldCapacity = elementData.length;
         // 扩容至原来的1.5倍
         int newCapacity = oldCapacity + (oldCapacity >> 1);
         // 再判断一下新数组的容量够不够,够了就直接使用这个长度创建新数组,
          // 不够就将数组长度设置为需要的长度
         if (newCapacity - minCapacity < 0)
             newCapacity = minCapacity;
         //若预设值大于默认的最大值检查是否溢出
         if (newCapacity - MAX_ARRAY_SIZE > 0)
             newCapacity = hugeCapacity(minCapacity);
         // 调用Arrays.copyOf方法将elementData数组指向新的内存空间时newCapacity的连续空间
         // 并将elementData的数据复制到新的内存空间
         elementData = Arrays.copyOf(elementData, newCapacity);
     }

所以我们可以清晰地看出,ArrayList扩容的本质就是计算出新的扩容数组的size后实例化,并将原有数组内容复制到新数组中去。这就是ArrayList的底层扩容机制。