浅谈ArrayList机制

浅谈ArrayList机制

ArrayuList这个东西在代码中随处可见,因为他被称为动态数组 既可以像数组那样快速访问集合内部元素,又能够动态的增删。他的容量可以实现自增。但因其内部由数组实现,所以增删有比较消耗性能。


构造方法(Constructor)

  1. 认识内部基本属性
	
    //ArrayList的默认初始化容量
	private static final int DEFAULT_CAPACITY = 10;

    private static final Object[] EMPTY_ELEMENTDATA = {};

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

   //定义不可被序列化的数组,也是ArrayList存储元素的本质
    transient Object[] elementData; 

  	// ArrayList的元素个数
    private int size;
  1. ArrayList的构造方法
    • 无参构造
    //构造一个容量为10,且元素数为空的数组列表(ArrayList)
    public ArrayList() {
            this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
        }
    

    这里要解释下:DEFAULTCAPACITY_EMPTY_ELEMENTDATA 不是一个空数组吗,哪来的元素为10了。这得听我娓娓道来:

    在创建集合后,第一次添加元素会给内部数组的容量设置为10,DEFAULTCAPACITY_EMPTY_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);
        }
    }
    

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

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

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

扩容机制

当我们探讨扩容时,肯定要从ArrayList的add方法走起,让我们来看看吧。

当我们 编写以下代码时:

ArrayList list = new ArrayList<>(); list.add(100); java内部方法调用顺序如下 :(注意下面源码是java17的,想看java8可以自行去看)具体实现扩容思路大同小异

public boolean add(E e) {
		modCount
        add(e, elementData, size);
        return true;
    }


private void add(E e, Object[] elementData, int s) {
        if (s == elementData.length)
            elementData = grow();
        elementData[s] = e;
        size = s + 1;
    }



private Object[] grow(int minCapacity) {
        int oldCapacity = elementData.length;
        if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            int newCapacity = ArraysSupport.newLength(oldCapacity,
                    minCapacity - oldCapacity, /* minimum growth */
                    oldCapacity >> 1           /* preferred growth */);
            return elementData = Arrays.copyOf(elementData, newCapacity);
        } else {
            return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
        }
    }

扩容分析:

当oldCapacity为0时,右移后还是0,也就是说此时扩容的大小为0+max(1,0)=1,容量从0扩展到1。那么什么时候是这种情况呢?

(1)传容量的构造方法传入的是0时,elementData被赋予的是EMPTY_ELEMENTDATA,此时数组容量为0,添加元素时,符合if的条件,会进入此扩容情况,容量从0扩展到1。

(2)传Collection元素列表的构造方法被传入空列表时,elementData被赋予的是EMPTY_ELEMENTDATA,数组容量为0,此时添加元素时,符合if的条件,会进入此扩容情况,容量从0扩展到1。

当oldCapacity大于0时,新创建的数组大小是老容量+老容量的一半,也就是老容量的1.5倍,每次扩容到原来的1.5倍。

else就剩一种情况了,也就是用默认无参构造方法创建的数组的初始扩容情况。此时的容量为0,添加一个元素时会创建一个新的数组,其大小为max(DEFAULT_CAPACITY, minCapacity)。

我们从上面的源码变量信息中可得知DEFAULT_CAPACITY是一个常量,其值为10,而minCapacity的值为(0+1),所以添加一个元素时,max(DEFAULT_CAPACITY, minCapacity)的值必为10。也就是说,当我们用默认无参构造方法创建的数组在添加元素前,ArrayList的容量为0,添加一个元素后,ArrayList的容量就变为10了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值