ArrayList集合

1.1 集合类主要分为Set集合、List集合和Map集合

  1. Collerction:Collerction是集合List、Set、Queue基本的接口。
  2. Iterator:迭代器,可以通过迭代器遍历集中的数据,不过现在基本用增强for来进行遍历。
  3. Map:是映射表的基础接口

1.2 List

  List是比较常用的数据类型,List是有序的Collection,List一共三个实现类:ArrayList、Vector和LinkedList

1.2.1 ArrayList

  ArrayList是List集合中最常用的实现类,它是通过数组实现,排列有序,可以重复,允许对元素进行快速随机访问,数组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加存储能力,就要将已经有数组的数据复制到新的存储空间中,当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价较高,因此它适合随机查找和遍历(getter()和setter()方法快),不适合插入和删除,当容量不够时,ArrayList是当前容量*1.5+1(动态增长的动态数组)。同时它的操作是线程不安全的,从源码中可以看出,ArrayList的实现主要是用了一个Object的数组,用来保存所有的元素,以及一个size变量用来保存当前的数组中已经添加了多少元素,在最终要的ADD操作源码如下:

public boolean add(E e) {

    /**
     * 添加一个元素时,做了如下两步操作
     * 1.判断列表的capacity容量是否足够,是否需要扩容
     * 2.真正将元素放在列表的元素数组里面
     */
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}
ensureCapacityInternal方法的作用是判断当前的新元素添加到数组列表的后面之后,列表的elementData数组的大小是否满足,如果size+1的这个需求长度大于elementData这个数组的长度,那么就对这个数组进行扩容。
所以在add方法中做了两个步骤,
  1.判断elementData数组的容量是否足够;
  2.在elementData中对应的位置上设置值;
这样在多线程进行add操作时就可能会导致elementData数组越界,逻辑如下:
    1. 列表大小为9,即size=9
    2. 线程A开始进入add方法,这时它获取到size的值为9,调用ensureCapacityInternal方法进行容量判断。
    3. 线程B此时也进入add方法,它获取到size的值也为9,也开始调用ensureCapacityInternal方法。
    4. 线程A发现需求大小为10,而elementData的大小就为10,可以容纳。于是它不再扩容,返回。
    5. 线程B也发现需求大小为10,也可以容纳,返回。
    6. 线程A开始进行设置值操作, elementData[size++] = e 操作。此时size变为10。
    7. 线程B也开始进行设置值操作,它尝试设置elementData[10] = e,而elementData没有进行过扩容,它的下标最大为9。于是此时会报出一个数组越界的异常ArrayIndexOutOfBoundsException.

 

posted @ 2019-07-28 22:16  Mr-Ran  阅读(283)  评论(0编辑  收藏  举报