ArrayList

一. 存储特性

  • 有序且可重复

1.为什么有序,且可重复??

  • ArrayList底层是Object数组,数组是有序的,所以有序;
  • 并且在执行添加的时候,底层并未对值做任何判断,而是直接添加,所以可重复

2. 该集合底层是数组,那么该数组初始长度是多少??

  • JDK1.6之前,底层数组长度为10;
  • 之后,底层数组初始长度为0

3. 如何入手查看源码??

(1) 看核心属性

  • private static final int DEFAULT_CAPACITY = 10;
    • 默认数组大小
  • private static final Object[] EMPTY_ELEMENTDATA = {};
    • 空数组
  • private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    • 默认空数组
  • transient Object[] elementData;
    • 底层存储元素的数组
  • private int size;
    • 集合存储的元素个数

(2) 看核心构造器

  • public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    • 无参创建集合,底层用默认空数组直接指向底层数组
  • 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,指定初始大小创建数组,指向底层数组
    • 实参等于0,直接用空数组,指向底层数组

 (3) 看核心方法

  • 按流程进入

 

 

 

 

 4. ArrayList的扩容原理是怎么样的,是怎么进行过扩容的???

  • 把原来(旧)的数组中的数据复制到一个新的,且内存空间更大数组中
  • 再将元素添加数组中

5. ArrayList为什么是线程安全的还是不安全的???

  • 首先该集合是线程不安全
  • 其底层对于主方法没有加锁
  • 所以会出现多线程导致的数据丢失问题
  • 举例:
    • 因为底层主方法没有锁
    • 所以会出现A.B线程因CPU调度原因,都在第一次扩容时,进入了扩容方法,
    • 两者也执行完扩容方法,准备添加元素,此时size为0;
    • 此时A开始添加完元素,还未及时进行size++
    • CPU调度到了B线程,此时size还是0,所以B线程将索引为0的A元素给替换掉了
    • 就导致A数据丢失的问题

6. 如何避免ArrayList集合的并发,线程安全问题

  • 方法一: 使用Collections.synchronizedList()方法对ArrayList对象进行包装
    • ArrayList<Integer> arraylist = Collections.synchronizedList(new ArrayList());
    • 此方法对SynchronizedList源码主要方法中都加了synchronized锁,保证单线程执行
    • 但是这个锁的力度很大,效率比较低
  • 方法二: 使用并发容器CopyOnWriteArrayList
    • CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<Integer>();
    • 此方法使用了Lock锁,对源码主要方法中都加了锁,保证单线程执行

7. ArrayList的优缺点,以及什么情况下使用该集合???

  • 优点: 因为有索引,所以可以随机访问元素效率高
  • 缺点: 因为是有序的,所以增删麻烦,效率低
    •   增删时,因为需要整体元素进行移位,会不断进行ArrayCopy到新数组,非常消耗时间
  • 如果访问元素更频繁的话,建议使用ArrayList
  • 如果插入修改元素比访问元素更频繁的话,建议使用LinkedList

8. 如何复制某个ArrayList到另一个ArrayList中去??

  • 第一种: 使用clone()方法,比如ArrayList newArray = oldArray.clone();
  • 第二种: 使用ArrayList构造方法,比如:ArrayList myObject = new ArrayList(myTempObject);
  • 第三种: 使用Collections的copy方法

9. ArrayList,Vector和LinkedList的区别??

  • ArrayList线程不安全,底层为数组,增删慢,查找快,可通过Collections.synchronizedList()实现线程同步
  • LinkedList线程不安全,底层为链表,增删快, 查找慢,可通过Collections.synchronizedList()实现线程同步
  • Vector线程安全,增删慢,查找快

10. 如下代码有什么问题? 能正常运行吗?? 如何解决??

 1  ArrayList<String> array = new ArrayList<>();
 2         array.add("aa");
 3         array.add("cc");
 4         array.add("bb");
 5         array.add("ee");
 6         array.add("ff");
 7 
 8         //CopyOnWriteArrayList<String> copyOnWriteArrayList =new CopyOnWriteArrayList(array);
 9 
10         for (String o : array) {
11             if("bb".equals(o)){
12                 array.remove(o);
13             }
14         }

 

  • 首先数据少有时候可以正常运行
  • 但是大部分情况会报错,出现JUC并发修改异常
  • 调用remove()方法导致迭代器modCount和expectedModCount的值不一致。就会出现并发修改异常

解决方案:

  • 普通for循环,倒叙遍历删除
  • 使用CopyOnWriteArrayList

 

posted @ 2021-01-31 21:21  卷神一代  阅读(166)  评论(0编辑  收藏  举报