java集合之ArrayList

前面说了集合中的接口和抽象类,终于走到了具体的实现类了,ArrayList是一个相对与java中的数组来说的动态数组。与Java中的数组相比,它的容量能动态增长,这里我一jdk8为例来分析ArrayList:
在这里插入图片描述

1、重要属性
 private static final long serialVersionUID = 8683452581122892189L;

    /**
     * 默认初始化容量
     */
    private static final int DEFAULT_CAPACITY = 10;
    /**
     * 一个空的数组对象
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * 空数组对象,当使用默认构造函数时,默认值为该值
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * 存储元素所使用的数组,数组的长度就是能容纳元素的个数,
     * 初始为DEFAULTCAPACITY_EMPTY_ELEMENTDATA,当添加第一个元素的时候,容量扩容为DEFAULT_CAPACITY
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     *数组中已经存放的元素个数
     */
    private int size;
2、构造器
    /**
     *带初始化长度的构造方法,比如我传入的初始化长度是2,那么数组长度就是2 
     */
    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);
        }
    }

    /**
     * 无参构造,将创建一个空数组,容量为0
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     *将Collection对象转换成数组,然后将数组的地址赋值给elementData。
		更新size的值,如果size的值等于0直接将内部空对象EMPTY_ELEMENTDATA的地址赋值给elementData。
		如果size的值大于0,则执行Arrays.copy方法,把Collection对象的内容copy到elementData中
     */
    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;
        }
    }
3、添加元素以及怎么扩容

首先,如果我们使用的是默认的构造函数,那么elementData的初始容量是为0的,那么我们要往里面添加元素怎么放的进去呢,猜测是在添加元素的时候对数组进行初始化的,那么扩容怎么扩呢?当数组的元素个数到多少的时候再次进行扩容呢?每次进行扩容的时候扩多少呢?
首先我们从添加元素开始:

/**
* size 初始值为0
*/
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}
/**
* 确保内部容量,顾名思义就是要保证数组容量,以确保正确的把内容存放进数组
*/
private void ensureCapacityInternal(int minCapacity) {
   ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
/**
* 计算容量,如果是第一次添加元素,elementData是空数组,没有容量,minCapacity为1
*
*/
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
    	// 首次进入,取大值,所以返回的是默认初始值DEFAULT_CAPACITY = 10
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    // 非首次进入则返回的是minCapacity
    return minCapacity;
}
/**
* 通过calculateCapacity计算得到的minCapacity=10,elementData.length为0 所以会进入grow
*
*/
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
/**
* 对数组进行扩容 
*
*/
private void grow(int minCapacity) {
    // overflow-conscious code
    // 首次扩容 oldCapacity = 0 
    // 第二次扩容是当元素添加到第11个的时候,此时oldCapacity=10
    int oldCapacity = elementData.length;
    // 首次扩容 newCapacity = 0 
    // 第二次扩容是当元素添加到第11个的时候,此时newCapacity=15
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 首次扩容 满足newCapacity - minCapacity < 0  所以 newCapacity = 10
    // 第二次扩容是当元素添加到第11个的时候,此时newCapacity=15 minCapacity=11
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    // 首次扩容 不满足newCapacity - MAX_ARRAY_SIZE > 0  所以 不会进hugeCapacity
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    //扩容将旧数组的内容复制到新数组,并将新数组地址赋值给elementData
    elementData = Arrays.copyOf(elementData, newCapacity);
}
3、删除元素

对于删除元素,我们重点看看批量删除【removeAll】

/**
* 删除当前结合中在c集合中的所有元素
*/
public boolean removeAll(Collection<?> c) {
    Objects.requireNonNull(c);
    return batchRemove(c, false);
}
private boolean batchRemove(Collection<?> c, boolean complement) {
   final Object[] elementData = this.elementData;
    int r = 0, w = 0;
    boolean modified = false;
    try {
        for (; r < size; r++)
        	// 核心在这里,当判断c集合中不包含elementData[r]时,就开始往前移动元素,相当于包含的就被覆盖了
        	// r顺序循环原始数组,w记录需要覆盖的位置
            if (c.contains(elementData[r]) == complement)
                elementData[w++] = elementData[r];
    } finally {
        // Preserve behavioral compatibility with AbstractCollection,
        // even if c.contains() throws.
        if (r != size) {
            System.arraycopy(elementData, r,
                             elementData, w,
                             size - r);
            w += size - r;
        }
        if (w != size) {
            // clear to let GC do its work
            for (int i = w; i < size; i++)
                elementData[i] = null;
            modCount += size - w;
            size = w;
            modified = true;
        }
    }
    return modified;
}

/**
* 删除单个元素,就是找到对应元素,把当前位置的元素置空,并往前移动后续元素
*/
public boolean remove(Object o) {
   if (o == null) {
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                fastRemove(index);
                return true;
            }
    } else {
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                fastRemove(index);
                return true;
            }
    }
    return false;
}
/**
* 删除指定位置的元素,把当前位置的元素置空,并往前移动后续元素
*/
 public E remove(int index) {
   rangeCheck(index);
    modCount++;
    E oldValue = elementData(index);
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // clear to let GC do its work

    return oldValue;
}
private void fastRemove(int index) {
    modCount++;
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // clear to let GC do its work
}
4、线程安全

Arraylist不是线程安全的集合

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值