ArrayList、LinkedList、Vector、CopyOnWriteArrayList的区别和源码分析
1. ArrayList
ArrayList 是一个数组队列,相当于动态数组。与Java中的数组相比,它的容量能动态增长。它继承于AbstractList,实现了List, RandomAccess, Cloneable, java.io.Serializable这些接口。默认容量是10(从源码中可以看出每次容量扩大为原来的1.5倍,int newCapacity = oldCapacity + (oldCapacity >> 1);)。ArrayList中的操作不是线程安全的!所以,建议在单线程中才使用ArrayList,而在多线程中可以选择Vector或者CopyOnWriteArrayList。
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { ...... /** * Default initial capacity. */ private static final int DEFAULT_CAPACITY = 10; 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); } } public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } public ArrayList(Collection<? extends E> c) { ...... } private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); } public int size() { return size; } ...... }
1.1 ArrayList的扩容
ArrayList的默认容量为10,当插入第11个元素的时候就会扩容,每次扩容增大为原来的1.5倍,可从代码int newCapacity = oldCapacity + (oldCapacity >> 1);
看出。
2. LinkedList
LinkedList和ArrayList一样实现了List接口,但是LinkedList是用双向链表实现的。
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable { transient int size = 0; transient Node<E> first; transient Node<E> last; public LinkedList() { } private static class Node<E> { E item; Node<E> next; Node<E> prev; Node(Node<E> prev, E element, Node<E> next) { this.item = element; this.next = next; this.prev = prev; } } ...... }
2.1 ArrayList和LinkedList的区别
- 遍历都可以使用普通for循环、foreach循环、Iterator接口实现
- ArrayList基于数组实现,LinkedList基于双向链表实现
- ArrayList随机访问和修改的效率比较高,LinkedList插入和删除的效率比较高
3. Vector
Vector是线程安全的,从源码中有很多的synchronized就可以看出。Vector是Java早期提供的线程安全的动态数组,synchronized关键字几乎修饰了所有对外暴露的方法,所以在读远大于写的操作场景中,Vector将会发生大量锁竞争,从而给系统带来性能开销。适用于写大于读的场景。
public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { ...... protected Object[] elementData; protected int elementCount; protected int capacityIncrement; public Vector(int initialCapacity) { this(initialCapacity, 0); } public Vector() { this(10); } public synchronized int capacity() { return elementData.length; } public synchronized int size() { return elementCount; } ...... }
ArrayList和Vector的区别
- Vector是线程安全的,源码中有很多的synchronized可以看出,而ArrayList不是。所以在单线程中,ArrayList比Vector性能要高。
- ArrayList和Vector底层都采用数组进行连续存储,当存储空间不足需要扩容的时候,ArrayList默认增加为原来的1.5倍,Vector默认增加为原来的2倍;
- 查找一个指定位置的数据,vector和arraylist使用的时间是相同的,都是0(1),这个时候使用Vector和ArrayList都可以
4. CopyOnWriteArrayList
CopyOnWriteArrayList具有如下特性:
- 实现了List接口;
- 内部持有一个ReentrantLock lock = new ReentrantLock();
- 增加时复制出一个新的数组,完成插入、修改或者移除操作后将新数组赋值给array;
- 增删改都需要获得锁,并且锁只有一把,而读操作不需要获得锁,支持并发。为什么增删改中都需要创建一个新的数组,操作完成之后再赋给原来的引用?这是为了保证get的时候都能获取到元素,如果在增删改过程直接修改原来的数组,可能会造成执行读操作获取不到数据;
- 内部使用ReentrantLock保证线程安全;
CopyOnWriteArrayList是java.util.concurrent包提供的方法,它实现了读操作无锁,写操作则通过操作底层数组的新副本来实现,是一种读写分离的并发策略。
CopyOnWrite它的原理是,任何修改操作,如add、set、remove,都会拷贝原数组,修改后替换原来的数组,通过这种防御性的方式,实现另类的线程安全。
所以这种数据结构,相对比较适合读多写少的操作,不然修改的开销还是非常明显的,适用于读远大于写的场景。
public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { private static final long serialVersionUID = 8673264195747942595L; /** The lock protecting all mutators */ final transient ReentrantLock lock = new ReentrantLock(); /** The array, accessed only via getArray/setArray. */ private transient volatile Object[] array; public boolean add(E e) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len + 1); newElements[len] = e; setArray(newElements); return true; } finally { lock.unlock(); } } } ...... }