Java—ArrayList数据结构及扩容机制

ArrayList数据结构

ArrayList底层用数组实现。

数组的特点:

  1. 查找快:按下标查找,时间复杂度为O(1)
  2. 删除慢:删除时需要将删除位置后面所有的元素向前移动一个位置,时间复杂度为O(n)
  3. 插入:
    • 非尾部插入慢:需要将插入位置后面的元素全部向后移动一位,时间复杂度为O(n)
    • 尾部插入快:直接插入到数组arr[arr.length]位置,时间复杂度为O(1)

在这里插入图片描述

实现的接口

  • List:定义了集合的常用操作

  • 标记接口:

    1. RandomAccess:标记接口,说明该类支持随机访问,大部分是基于数组实现

    2. Cloneable:说明该类支持拷贝,ArrayList的clone()方法。

      • 浅拷贝:拷贝出来的对象不是独立的对象,相当于只是拷贝了栈中的引用,引用指向的对象还是同一个对象。

      • 深拷贝:创建出一个新的对象

    3. Serializable:序列化,将对象存入磁盘持久化或者网络传输

构造方法

  • ArrayList(int initialCapacity):指定数组容量构造方法

     /**
         * 指定数组容量创建数组
         */
        public ArrayList(int initialCapacity) {
            //大于0按传入创建,等于0直接指向空数组,负数抛异常
            if (initialCapacity > 0) {
                this.elementData = new Object[initialCapacity];
            } else if (initialCapacity == 0) {
                this.elementData = EMPTY_ELEMENTDATA;
            } else {
                throw new IllegalArgumentException("Illegal Capacity: "+
                                                   initialCapacity);
            }
        }
    
    
  • ArrayList():无参构造方法

    /**
     * 午餐构造方法,常用,指向默认空数组(节省内存空间,new多个对象不添加元素时,只会有一个数组)
     */
    public ArrayList() {
        //创建的时候数组指向默认的空数组
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    
  • ArrayList(Collection<? extends E> c):根据一个集合构建ArrayList

     /**
         * 根据集合创建list,如果传入的集合为空,直接将数组指向默认数组
         */
        public ArrayList(Collection<? extends E> c) {
            //将传入集合转为数组
            elementData = c.toArray();
            if ((size = elementData.length) != 0) {
                // c.toArray might (incorrectly) not return Object[] (see 6260652)
                if (elementData.getClass() != Object[].class)
                    elementData = Arrays.copyOf(elementData, size, Object[].class);
            } else {
                // 如果传入的集合元素为0,指向默认空数组
                this.elementData = EMPTY_ELEMENTDATA;
            }
        }
    
    

add方法和扩容

当ArrayList被创建(调用无参构成方法),将数组默认指向默认的数组(DEFAULTCAPACITY_EMPTY_ELEMENTDATA)。此时List的数组是一个空数组。调用add方法,调用扩容方法:有两种情况,需要扩容不需要扩容。如果将要添加的数组元素个数大于数组的长度时,需要扩容(调用默认构造方法,第一次调用add都需要进行扩容)。如果小于则不进行扩容,直接使用尾插法将元素添加到数组末位。

数组扩容逻辑,源码grow()方法。默认扩容之前数组大小的1.5倍,如果是默认创建,则第一次创建一个数组容量为10的数组,如果非第一次扩容,如果扩容1.5被小于元素个数,则按元素个数(+将添加的个数)扩容。根据计算好的新数组容量,调用工具类Arrays.copyOf方法创建新数组,将旧元素赋值到新数组上。

	public boolean add(E e) {
    	//扩容,如果调用默认构造方法,第一次进来elementData数组是个空数组。数组无法改变长度。size默认为0
   	 	ensureCapacityInternal(size + 1); 
    	//存放元素,size是元素个数
   	 	elementData[size++] = e;
   	 	return true;
	}
 	/**
     * 调用默认构造方法时,第一次调用add触发扩容minCapacity为1
     */
    private void ensureCapacityInternal(int minCapacity) {
        //判断数组elementData是否为默认数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA(调用默认构造方法是会指向它)
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            //如果指向默认数组,取默认值10和传入的minCapacity大的值
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
		
        //扩容
        ensureExplicitCapacity(minCapacity);
    }
	
	/**
	 *扩容
	 */
    private void ensureExplicitCapacity(int minCapacity) {
        //操作数 + 1
        modCount++;

        //如果元素个数小于数组长度则不进行扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
	
	/**
  	 * 扩容
     */
    private void grow(int minCapacity) {
        //获取数组之前的长度
        int oldCapacity = elementData.length;
        //通过之前数组长度算出之后的数组长度。扩容1.5倍
        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);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值