今天闲来无事,就简单分享一下Arraylist的源码分析

背景

现在看 一个简单的main函数,直接上代码

public class ArrayList{
    public static void main(String[] args){
        List list = new ArrayList();
        
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("e");
        list.add("f");
        list.add("g");
        list.add("h");
        list.add("i");
        list.add("j");
      
        //往集合中添加第11个元素
        list.add("k");
  
        //集合中获取指定位置元素
        Object o1= list.get(0);
        System.out.println(o1);
    }
}

对main函数进行简单分析

先看ArrayList这个类里的成员变量

 /**
     * Default initial capacity.
     */
    private static final int DEFAULT_CAPACITY = 10;
 
    /**
     * Shared empty array instance used for empty instances.
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};
 
    /**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
 
    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     */
    transient Object[] elementData; // non-private to simplify nested class access
 
    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     */
    private int size;

构造方法处打个断点,然后debug启动,构造里面走了这个方法,这个常量是一个空Object数组,赋值给了成员属性elementData,它也是一个Object数组,构造方法里就是做了这个事情。说明ArrayList底层用的是一个Object数组。

public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

debug进入源码

添加list.add(“a”);

进入源码

public boolean add(E e) {
        //size是0,0+1=1 是入参
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

查看ensureCapacityInteranl方法

 private void ensureCapacityInternal(int minCapacity) {
         //初始化的时候,已经把这个常量赋值给了这个elementData,所以他们两个是相等的
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        //取这两个值的最大值,默认容量这个值是10,10和1比较,10大的,取10
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
 
        ensureExplicitCapacity(minCapacity);
    }

接着进入ensureExplicitCapactiy()方法中

  private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        //传进来的是10,elementData是一个空的数组,所以length是0,也就是10-0。第一次扩容后数组长度是10,所以在小于10个元素添加的时候,此处都是小于0,所以就不会走扩容,当minCapacity大于11的时候,此处就大于0,走扩容grow方法。
        if (minCapacity - elementData.length > 0)
        //扩容方法,第一给数组的容量是10个。以后每次扩容都是数组容量的1.5倍数,数组容量+数组容量/2
            grow(minCapacity);
    }

进入grow方法

 private void grow(int minCapacity) {
        
        // 此刻length是0,
        int oldCapacity = elementData.length;
        //新的容量=老的容量向右移动一位,这块就是通常所说的扩容1.5倍的原理,这个是newCapacity也是0 
        //oldCapacity>>1  等于oldCapacity/2,右移几位就是除以2的几次方
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 0 减去传进来的10 小于0
        if (newCapacity - minCapacity < 0)
        //然后把10赋值给newCapacity,新数组容量是10,//如果初始的时候,我们不设定,这个容量就是10。开辟一块长度为10的数组。
            newCapacity = minCapacity;
        ///10-最大数组长度2147483639,不大于0,所以不会走下面这个方法。
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        
        //有了这个长度是10的数组,我们需要把原来数组元素copy到新的数组当中
        elementData = Arrays.copyOf(elementData, newCapacity);
        
    }

最后回到add方法

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //添加第一个元素成功,然后size值加1,从0变为1。
        elementData[size++] = e;
        return true;
    }

第一个元素a 添加成功了。接下来一直添加是一样的,小于10个元素的时候是不需要扩容的 。但是加到第十一个的时候,变了。当数组添加元素超过10个的时候,这时候就要扩容,扩容的大小是容量大小的1.5倍。然后把老数组的元素copy到新数组里。

以上就是添加元素的源码分析。

下面再说下,获取元素的源码。进入get方法中

public E get(int index) {
        //先判断索引是否在长度范围内
        rangeCheck(index);
 
        return elementData(index);
    }

进入rangeCheck方法,如果索引值大于数组长度,就会抛出异常数组角标越界

 private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

如果没有越界,进入elementData方法中,根据下标直接取元素,并返回。

E elementData(int index) {
        return (E) elementData[index];
    }

以上就是ArrayList的源码分析,总结下就是,第一次扩容给数组长度10,然后添加元素个数小于10,不会扩容个,当添加元素个数大于10的时候,就会扩容,扩容是数组长度的1.5倍。获取是直接根据角标获取,先判断下角标是否在数组长度范围内,超过长度范围内,就抛出角标越界,在范围内,就根据下标,直接去取元素。

写在最后

ArrayList数据结构,相信每个开发人员都用到过,但是真正领会其中含义,看过或分析过源代码的肯定不多。

想了解更多知识,请关注我吧_

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值