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