从零开始带你成为Java高手学习笔记(一)ArrayList是如何设计的

ArrayList源码分析

1、增加元素

ArrayList有两个不同的add()方法。常用的就是第一个,添加元素到list的末尾,只分析第一个方法。

 /**
      * 将指定的元素添加到列表末尾.
      *
      * @param e element to be appended to this list
      * @return <tt>true</tt> (as specified by {@link Collection#add})
      */
     public boolean add(E e) {
         //确认List容量
         ensureCapacityInternal(size + 1);  // Increments modCount!!
         //把元素放到List的末尾
         elementData[size++] = e;
         return true;
     }

来看ensureCapacityInternal(size + 1); // Increments modCount!!

执行的是以下方法,size是当前ArrayList的大小0:

 private void ensureCapacityInternal(int minCapacity) {
     ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));  //calculateCapacity执行的是下面的静态方法calculateCapacity
 }
 ​
 private static int calculateCapacity(Object[] elementData, int minCapacity) {
     if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
         return Math.max(DEFAULT_CAPACITY, minCapacity);
     }
     return minCapacity;
 } 

calculateCapacity方法中对elementData进行检查,如果它是DEFAULTCAPACITY_EMPTY_ELEMENTDATA的,就返回size和DEFAULT_CAPACITY两者中的最大值,第一次添加元素时,扩容到默认值10。

调用无参构造的时候,把DEFAULTCAPACITY_EMPTY_ELEMENTDATA赋值给了 elementData,此时一定是它。

 /**
 * Constructs an empty list with an initial capacity of ten.
 */
 public ArrayList() {
 this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
 }

DEFAULT_CAPACITY默认为10.

 /**
 * Default initial capacity.
 */
 private static final int DEFAULT_CAPACITY = 10;

源码中对elementData的注释写的很清楚,如果 一个ArrayListelementDataDEFAULTCAPACITY_EMPTY_ELEMENTDATA,那么在第一次添加元素时就扩容为默认容量10.

   
  /**
      * 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

下面这一步就是很重要的扩容了,calculateCapacity返回ArrayList的大小之后,执行ensureExplicitCapacitymodCount++,然后判断元素的lengthminCapacity的大小,在第一次增加元素的时候,会进入grow

 private void ensureExplicitCapacity(int minCapacity) {
     modCount++;
 ​
     // overflow-conscious code
     if (minCapacity - elementData.length > 0)
         grow(minCapacity);
 }

扩充:

 /**
      * Increases the capacity to ensure that it can hold at least the
      * number of elements specified by the minimum capacity argument.
      *
      * @param minCapacity the desired minimum capacity
      */
 private void grow(int minCapacity) {
     // 获取当前数组容量
     int oldCapacity = elementData.length;
     //扩容,新数组容量 = 当前数组容量 + 当前数组容量/2
     //右移除以2的几次幂,左移乘以2的几次幂
     int newCapacity = oldCapacity + (oldCapacity >> 1);
     //如果扩充之后的容量还小于想要的容量,扩充容量就等于想要的容量
     if (newCapacity - minCapacity < 0)
         newCapacity = minCapacity;
     //拿扩充之后的容量和临界值比较,如果大于临界值,进行大容量分配
     if (newCapacity - MAX_ARRAY_SIZE > 0)
         newCapacity = hugeCapacity(minCapacity);
     // minCapacity is usually close to size, so this is a win:
     elementData = Arrays.copyOf(elementData, newCapacity);
 }

大容量分配:

 private static int hugeCapacity(int minCapacity) {
     if (minCapacity < 0) // overflow
         throw new OutOfMemoryError();
     //判断想要的是否大于临界值,如果大于,则返回Integer.MAX_VALUE(0x7fffffff),否则返回MAX_ARRAY_SIZE
     return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
 }

MAX_ARRAY_SIZE为什么要-8

因为某些VM会在数组中保留一些头字,尝试分配这个最大存储容量,可能会导致Array容量大于VMlimit,最终导致OutOfMemoryError

 /**
      * The maximum size of array to allocate.
      * Some VMs reserve some header words in an array.
      * Attempts to allocate larger arrays may result in
      * OutOfMemoryError: Requested array size exceeds VM limit
      */
 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

然后增加的过程就完事了。

 

未完待续。。。

很快!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值