ArrayList

ArrayList

  1. 底层是Object数组,是一个动态数组,其容量能自动增长。
  2. 是线程不安全的,只能用在单线程环境下,多线程环境下可以考虑用Collections.synchronizedList(List l)函数返回一个线程安全的ArrayList类,也可以使用concurrent并发包下的CopyOnWriteArrayList类。
  3. 查询速度快(底层是数组可按照索引获得元素),增删速度慢(添加、删除时该元素后面的所有元素都要移动,所以添加/删除数据效率不高)。

下面我们来分析ArrayList的源代码:

属性

 transient Object[] elementData; //存储元素
    private int size;//表示它包含的元素的数量。

ArrayList使用Object[] elementData来存储元素,size表示它包含的元素的数量。

无参构造器

public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
//默认初始容量
private static final int DEFAULT_CAPACITY = 10;
/**被用于默认大小的空实例的共享数组实例。
 *其与EMPTY_ELEMENTDATA的区是:
 *当我们向数组中添加第一个元素时,知道数组该扩充多少。
 */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

通过上面源码我们可以知道elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA={};说明当我们调用无参构造时,底层数组容量为0,当我们第一次添加数据时才进行扩容,默认初始容量为10。

指定初始容量构造器

 public ArrayList(int initialCapacity) {
 //如果传入的参数大于0,就创建一个容量为传入的参数的Object数组赋值给elementData
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
            //如果传入的参数等于0,就将空的数组赋值给elementData
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
        //抛出异常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

包含指定collection的构造器

 public ArrayList(Collection<? extends E> c) {
 //将Collection转化为数组赋给elementData。
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray可能(错误地)不返回Object[]
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // 替换为空数组。
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

分析源码我们可以知道,包含指定collection的构造器是将Collection转化为数组再赋给elementData。

下面我们来看一下它的扩容机制

当我们添加Collection时,我们可以看到它是遍历Collection,调用add(E e)方法。将集合的每个数据添加到ArrayList

public boolean addAll(Collection<? extends E> c) {
        boolean modified = false;
        //遍历Collection,将集合的每个数据添加到ArrayList
        for (E e : c)
            if (add(e))
                modified = true;
        return modified;
    }

当我们添加单个数据时,我们可以看到add(E e)方法中调用的ensureCapacityInternal((size + 1);来确定容量。

public boolean add(E e) {
         //确定容量
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //size++自增,并将添加的元素赋值给数组下标为size++的对象
        elementData[size++] = e;
        return true;
    }

ensureCapacityInternal(int minCapacity)方法,使minCapacity等于默认容量10和传入的容量中的较大值。然后调用了ensureExplicitCapacity(minCapacity);方法,判断是否扩容。

 private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        //判断是否扩容
        ensureExplicitCapacity(minCapacity);
    }

当传入的容量大于原来的数组容量时,调用grow(minCapacity);进行扩容。

private void ensureExplicitCapacity(int minCapacity) {
        //modCount表示这个列表的结构被修改的次数。
        modCount++;
        // 当传入的容量大于原来的数组容量时,进行扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

扩容

 private void grow(int minCapacity) {
        // 原来的数组容量
        int oldCapacity = elementData.length;
        //新的容量为原来的数组容量的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //如果新的容量还是小于传入的容量,则使新的容量==传入的容量
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
       //如果新的容量大于能分配的数组的最大大小,进入。
        if (newCapacity - MAX_ARRAY_SIZE > 0)
        //如果传入的容量大于能分配的数组的最大大小,则使新的容量==Integer.MAX_VALUE :
        //如果传入的容量小于能分配的数组的最大大小,则使新的容量==能分配的数组的最大大小
            newCapacity = hugeCapacity(minCapacity);
        //将原来的数组拷贝到新的数组
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

扩容为原来的数组容量的1.5倍,若扩容后的容量还是不够,直接使容量变为你所需要的容量。然后将原来数组中的数据拷贝到新的数组中。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值