ArrayList源码及常见问题分析

ArrayList的扩容问题

这里就要从ArrayList的源码说起。

ArrayList类的定义

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

从ArrayList类的定义可以看出,ArrayList继承了AbstractList并且实现了List,是一个数组队列,具有增、删、改、遍历等基本功能。
AbstractList还实现了RandomAccess接口,及RandomAccess可以进行随机访问。
什么是随机访问?
我理解的随机访问就是可以根据ArrayList元素下标获取元素。
ArrayList实现了Cloneable接口,可以被克隆。
ArrayList实现了java.io.Serializable接口,即ArrayList支持序列化,可通过序列化进行传输。
关于线程安全问题!!
ArrayList是线程不安全的集合,在多线程环境中,可使用线程安全的Vector或者CopyOnWriteArrayList这里建议使用CopyOnWriteArrayList)。

ArrayList源码中的属性

	// ArrayList的额序列化版本号
    private static final long serialVersionUID = 8683452581122892189L;

	// ArrayList的默认容量为10
    private static final int DEFAULT_CAPACITY = 10;

	// 空的对象数组
    private static final Object[] EMPTY_ELEMENTDATA = {};

	// 空的对象数组,当调用ArrayList的无参构造器时使用。
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

	// ArrayList存放数据的数组
    transient Object[] elementData;

	// ArrayList中的数据个数(与容量不同)
    private int size;

ArrayList的属性可以看出,ArrayList底层其实是一个动态数组,他与数组最大的区别就是ArrayList可以动态的扩容。ArrayList的扩容问题会在本文分析到。

ArrayList的构造函数

ArrayList有三个构造函数

指定ArrayList初始容量的构造器。

    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会抛出异常,当initialCapacity==0时,将ArrayList属性中的EMPTY_ELEMENTDATA赋值给elementData,这时,ArrayList底层其实就是一个空的容量为0数组队列。当initialCapacity>0会创建一个容量为initialCapacity大小的对象数组。

无参构造器

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

ArrayList的无参构造函数内容很简单,就是讲ArrayList类属性中的DEFAULTCAPACITY_EMPTY_ELEMENTDATA赋值给elementData,通过ArrayList的无参构造函数可以得到一个空的长度为0的数组队列。

参数为Collection的构造器

    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 {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

构造器首先调用Collection对象c的toArray方法,将对象c转换为Object数组并赋值为elementDataArrayList便初始化。

那么,ArrayList的初始容量为什么为10呢?ArrayList类中的DEFAULT_CAPACITY属性又在哪里被使用?接下来说到的ArrayList的扩容问题会解释道ArrayList的初始容量问题和DEFAULT_CAPACITY的使用。

ArrayList的扩容问题可以从ArrayListadd方法说起。
ArrayListadd方法有两个:

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

这里通过讲解第一个add方法分析ArrayList的扩容问题。

当通过使用add(E e)方法向集合中添加数据时,会先调用ensureCapacityInternal(size + 1);方法,
我们假设当前集合size==0,即空集合。

    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

ensureCapacityInternal(int minCapacity)中minCapacity参数为size+1即参数为1,ensureCapacityInternal方法再调用ensureExplicitCapacity(int minCapacity)方法。

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

ensureExplicitCapacity(int minCapacity)方法的参数minCapacity
calculateCapacity(elementData, minCapacity)方法的返回值。

    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

所以我们要先分析calculateCapacity方法,calculateCapacity方法传入elementData, minCapacity,这里minCapacity的值认为size+1为1,if判断条件成立,执行return Math.max(DEFAULT_CAPACITY, minCapacity);返回的便是DEFAULT_CAPACITY,即返回10。然后将返回的10作为ensureExplicitCapacity方法的参数,那么ensureExplicitCapacity方法的参数为10。
ensureExplicitCapacity方法中的if判断成立,调用grow(minCapacity);方法

    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        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);
    }

方法内部int oldCapacity = elementData.length,则oldCapacity ==0
这里涉及到位运算,>>1这里可以大致理解为扩大为原本的1.5倍,
那么 int newCapacity = oldCapacity + (oldCapacity >> 1),则newCapacity ==0
则第一个if判断条件成立,newCapacity = minCapacitynewCapacity ==10
最后执行elementData = Arrays.copyOf(elementData, newCapacity);,则elementData 被扩容为容量为10的对象数组。
至此,ArrayList的扩容到此结束,继续回到add(E e)方法,执行elementData[size++] = e;语句,将e放入
elementData 对象数组中。

扩容问题总结
这里笔者以刚被初始化的ArrayList集合,即size==0的的情况分析了ArrayList集合的扩容问题,只是为了展示ArrayList的扩容全过程,大家可以通过不同的size进行不同的分析,充分理解ArrayList的扩容过程。
这里值得一提的是,ArrayList默认初始容量(调用无参构造器创建的ArrayList对象)并不是10,而是0,当第一次放入数据时,ArrayList的容量会被扩容到10。

ArrayList的其它方法包括被重载的add方法就不再过多分析,大家理解了上述问题后,其它方法便会无师自通。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值