ArrayList源码解读

1、概述

ArrayList是Java集合中使用比较多的,继承AbstractList,实现List接口。内部维护一个Object类型的数组elementData.允许null的存在。其中实现了 List<E>、RandomAccess、`Cloneable`、java.io.Serializable.

需要深入了解它,便要从成员变量构造方法主要方法深入。

2、成员变量

private static final long serialVersionUID = 8683452581122892189L;

// 无参构造后第一次添加数据时,初始化数组elementData的容量 10 
private static final int DEFAULT_CAPACITY = 10; 

// 无参构造时,初始化数组elementData为空数组
private static final Object[] EMPTY_ELEMENTDATA = {};  
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

// ArrayList内部维护的数组,用来存储数据
transient Object[] elementData

// 实际元素个数
private int size;  
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

上面标红是比较主要的属性,size是elementData中实际有的元素个数,这里需要区别elementData的length是集合容量,表示最多可以容纳多少个元素。

//初始化elementData的容量为10
ArrayList list = new ArrayList(10);  
//输出 0
System.out.println(list.size()); 

其中还有一个属性也是比较重要的,不过不是它的,而是它的父类的属性。

protected transient int modCount = 0; 

modCount 是ArrayList 继承自 AbstractList 的属性,它是用来记录集合的尺寸被修改次数的,addremove 方法会导致 modCount +1 ,而set 方法不会,因为set 方法没有改变集合的尺寸。它的作用会在(fail-fast)快速-失败中体现。

3、构造方法

初始化时提供了三个方法

public ArrayList()
public ArrayList(int initialCapacity)
public ArrayList(Collection<? extends E> c) 

无参构造方法

/**
* Constructs an empty list with an initial capacity of ten.
 */
//DEFAULTCAPACITY_EMPTY_ELEMENTDATA 是一个{}空数组
 public ArrayList() {
      this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; 
} 

创建对象使用无参构造时,初始elementData容量为0,(在jdk7是10)如果第一次添加数据时,需要扩容,则扩容elementData为10,如果再次需要扩容(添加元素超过10后)则扩容elementData为1.5倍

有参构造方法

/**
 * Constructs an empty list with the specified initial capacity.
 */
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,如果initialCapacity > 0直接创建一个Object类型大小为initialCapacity的数组,initialCapacity=0时把EMPTY_ELEMENTDATA赋值elementData,initialCapacity < 0时就抛出异常。

指定Collection的参数有参构造

/**
 * Constructs a list containing the elements of the specified
 * collection, in the order they are returned by the collection's
 * iterator.
 */
public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        // defend against c.toArray (incorrectly) not returning Object[]
        // (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // replace with empty array.
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

Collection转为数组并赋值给elementData,把elementData中元素个数赋值给size。当 size != 0 时则在判断elementData的class类型是否是Object[],不是就做转换。size = 0时,把EMPTY_ELEMENTDATA赋值给elementData,就相当于new ArrayList();

4、ArrayList扩容机制(重点)

使用无参构造方法创建对象后,在第一次添加的分析,以代码deburg演示扩容的过程:代码如下

ArrayList list  = new ArrayList();
// 第一次扩容
for (int i = 0; i < 10; i++) {
   list.add(i);
}

由于使用deburg不好演示过程,再次就使用processon画图来演示过程。

注意:当扩容为原来的1.5倍,newCapacity - minCapacity <=0 并且 elementData不为空数组,minCapacity > 0 就直接返回minCapacity的值(这个值是size+1的容量

5、主要方法

  • add()方法

/**
 * Appends the specified element to the end of this list.
 */
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;
}

在add()方法中清楚地看到判断是否size(数组真实元素个数)和数组容量大小是否相等,相等的话就扩容。不相等就直接添加到数组中,size++;在这个方法中可以看到modCount这个变量是记录该数组修改的次数

说到grow扩容方法就看看它内部到底长什么样的.

  • grow()方法

private Object[] grow() {
    return grow(size + 1);
}
private Object[] grow(int minCapacity) {
    return elementData = Arrays.copyOf(elementData,
                                       newCapacity(minCapacity));
}

该方法首先把size+1作为参数传入grow的重载方法中,这个是添加一个元素时的最小容量值size+1),在后面的newCapacity方法中很有可能就扩容值为size+1.单说也不算,不如再看看newCapacity方法内部到底是什么机密。

Arrays.copyof(elementData,newCapacity(minCapacity)) 扩容并保存之前数组的数据,不可以扩容了就不保存之前的数据对吧。

  • newCapacity()方法

private int newCapacity(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity <= 0) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return minCapacity;
    }
    return (newCapacity - MAX_ARRAY_SIZE <= 0)
        ? newCapacity
        : hugeCapacity(minCapacity);
}

 oldCapacity保存数组之前的容量,minCapacity是size+1的值,对oldCapacity扩容为1.5倍,和minCapacity比较大小,如果newCapacity - minCapacity <=0,在判断是否为默认的空数组,如果是就返回扩容为10的值如果也不为空数组和minCapacity > 0 就直接返回minCapacity的值size+1)如果扩容后的newCapacity小于ArrayList的最大容量MAX_ARRAY_SIZE,就返回扩容为1.5倍的值。否则大于MAX_ARRAY_SIZE就调用hugeCapacity(minCapacity)方法,该方法是咋样处理呢?看下面方法分析过程。

  • hugeCapacity()方法

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE)
        ? Integer.MAX_VALUE
        : MAX_ARRAY_SIZE;
}

注意到传入的参数是minCapacity(size+1)的值,不是扩容为1.5倍的值,就比如minCapacity大于MAX_ARRAY_SIZE 在newCapacity()方法中newCapity更加大于MAX_ARRAY_SIZE,就调用hugeCapacity()方法,再看hugeCapacity()方法,返回Integer.MAX_VALUE的值.

6、ArrayList源码结论(重点)

  • ArrayList中维护了一个Object类型的数组elementData.  transient Object[] elementDatatransient 表示瞬间、短暂的,表示该属性不会被序列化
  • 当创建对象时,如果使用无参构造方法,则初始化elementData容量为0.(jdk7是10)
  • 添加元素时,先判断是否要扩容(s == elementData.length),需要则调用grow()等方法,否则直接添加元素
  • 如果使用无参构造方法创建对象,如果第一次添加元素,需要扩容则扩容elementData为10,需要再次扩容就是原先的1.5倍.
  • 如果使用有参构造方法初始化容量为Capacity,容量为Capacity值大小.
  • 如果使用有参构造指定容量Capacity,如果添加元素后需要在扩容,则直接扩容elementData为1.5倍.
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值