jdk1.8中 ArrayList 底层数组到底是怎么扩容的

一、结论先行

ArrayList在JDK1.8与JDK1.7底层区别
JDK1.7:ArrayList像饿汉式,直接创建一个初始容量为10的数组,当数组的长度不能容下所添加的内容时候,数组会扩容至原大小的1.5倍

JDK1.8:ArrayList像懒汉式,一开始创建一个长度为0的数组,当添加第一个元
素时再创建一个始容量为10的数组,当数组的长度不能容下所添加的内容时候,数组会扩容至原大小的1.5倍

二、JDK1.8 ArrayList源码分析
1、ArrayList 属性
    /**
     * 默认容量的大小
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 空数组常量
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * 默认的空数组常量,我们将其与EMPTY_ELEMENTDATA区分开来,
     * 以便知道添加第一个元素时需要膨胀多少
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};


    /**
     * 存放元素的数组,从这可以发现ArrayList的底层实现就是一个Object数组
     */
    transient Object[] elementData; 

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

   /**
     *数组的最大上限,超过上限可能导致OutOfMemoryError错误
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
2、构造方法
    /**
     * 指定大小的时候,elementData就变成了我们所指定的初始化大小了
     */
    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);
        }
    }

    /**
     *   根据上面的属性可知,DEFAULTCAPACITY_EMPTY_ELEMENTDATA={},所以默认创建的是   一个大小为0的空数组
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

从上面我可以知道,jkd1.8中默认的是大小为0空数组,这个和jdk1.7之前都是不一样的,这和设计模式的懒汉式很有相似之处

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

    /**
     * 将指定的参数添加到列表的末尾,其中size是数组中包含元素的个数
     * @param e 以附加到此列表中
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  
        // 数组的下标从0开始,所以size++保证elementData每次添加完一个元素,元素个数也随之加1
        elementData[size++] = e;
        return true;
    }

	// 参数minCapacity 表达的意思是所需数组的最小容量,也就是size+1, 上面传的值
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }


   /**
   * 计算容量的方法,
   */
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
      // 如果是一个空数组,
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        // DEFAULT_CAPACITY从属性可知默认是10,minCapactity为数组的大小
        // 由此可以看出他是在添加第一个元素的时候,才创建了长度为10的数组
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }


	private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
		 // 如果此时所需要的最小的长度大于原数组的长度,则需要进行扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    
   /**
     * 增加容量,以确保它至少可以容纳minCapacity指定的元素数量。
     * @param minCapacity 表示所需要的扩容的量
     */
    private void grow(int minCapacity) {
        int oldCapacity = elementData.length;  // 原数组的长度
      //原数组的长度+原数组的长度/2,表示扩容了原来大小的1.5倍,newCapacity :表示需要扩容的量
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
     // 如果需要的扩容量大于了本类中定义的最大扩容限制,则扩容到 int 类型最大长度
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
     // 调用的是数组的复制方法,实现扩容
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
      // 如若需要扩容的量大于最大限制,则扩容量改为 int 最大限制量:2147483647,否则为本类中所限制长度:2147483647-8
        return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;
    }

解释:int newCapacity = oldCapacity + (oldCapacity >> 1);
这个>>1 表示的是右移一位,转为二进制我们应该一下就明白了:比如16,转为2进制为
10000,右移一位则成为01000,换为十进制就是8,扩容的大小也就相当于oldCapacity +oldCapacity /2了

4、总结

jdk1.8中:
add方法总结起来就是在插入数据之前,会先检查是否需要扩容,通过无参构造方法来创建 ArrayList 时,它的大小其实是为 0 的,只有在使用到 的时候,才会通过 grow 方法去创建一个大小为 10 的数组。

public boolean add(E e) 方法的复杂度为O(1),涉及到扩容的操作是非常少的,可以忽略不计,它的本质是添加元素到数组中最后一个元素的后面。

public void add(int index, E element) 这个是带指定下标的add 方法,复杂度为O(n),因为涉及到数组中元素的移动,这一操作非常耗时,由此可见ArrayList不适合插入和删除操作。

三、ArrayList与Vector的区别

现在Vector已经很少有人用了,这里只是简单的记录下二者区别:

1、ArrayList线程不安全,Vector是线程安全的
通过Vector源码我们可以知道很多方法都是加了synchronized关键字,所以Vector是线程安全的。

2、ArrayList创建的默认大小为0,Vector创建时的默认大小是10。

3、ArrayList 每次扩容都以当前数组大小的 1.5 倍去扩容, Vector 每次扩容都以当前数组大小的 2 倍去扩容。当指定了 capacityIncrement 之 后,每次扩容仅在原先基础上增加 capacityIncrement 个单位空间。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值