Vector、ArrayList、LinkedList区别

Vector、ArrayList、LinkedList区别

一月 14, 2020

前言

ArrayListVectorLinkedList都是实现了List接口(允许数据重复)的容器类,它们都能对元素做增删改查的操作。

ArrayList

ArrayList是基于数组实现的,采用懒加载策略(第一次add时才初始化内部数组,默认初始化大小为10)。它允许对元素的快速随机访问以及在链表尾部进行插入或删除元素操作。但是当随机插入元素时,如果此时数组大小已经不能满足再插入元素时就会进行扩容操作【扩容为原来集合容量的1.5倍】,然后将已插入元素复制到新的存储空间中。如果从中间插入或删除数据势必会导致大量数据的复制,搬移等,这样的话代价会很高。由此看来,ArrayList适合随机访问,而不适合插入和删除。采用异步处理,线程不安全,性能较高

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
//ArrayList的元素是存储在缓冲数组中的,并且ArrayList的容量就是缓冲数组的长度
//任何一个属性值elementData==DEFAULTCAPACITY_EMPTY_ELEMENTDATA的ArrayList都会在第一次添加元素的时候扩容为默认容量DEFAULT_CAPACITY
transient Object[] elementData; // non-private to simplify nested class access

/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
//ArrayList的大小(所含元素的个数)
private int size;

/**
* Default initial capacity.
*/
//默认的容量
private static final int DEFAULT_CAPACITY = 10;

/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

/**
* Constructs an empty list with an initial capacity of ten.
*/
//构造一个空集合
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}


/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
//向集合尾部添加特定元素
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}

private void ensureCapacityInternal(int minCapacity) {
//第一次插入元素的情况,此时才开始初始化数组,长度为DEFAULT_CAPACITY(10)
//minCapacity=Math.max(10,1)=10;
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}

ensureExplicitCapacity(minCapacity);
}

/**
* The number of times this list has been <i>structurally modified</i>.
* Structural modifications are those that change the size of the
* list, or otherwise perturb it in such a fashion that iterations in
* progress may yield incorrect results.
*/
//modCount记录的是list的结构被修改的次数
//结构被修改指的是改变了list的大小【添加或者删除元素的时候】
//否则,如果在进行迭代器循环的时候如果修改了modCount的值可能会导致错误的结果
protected transient int modCount = 0;


private void ensureExplicitCapacity(int minCapacity) {
//modCount=0
modCount++;
//modCount=1

// overflow-conscious code
//加上正准备插入的这个元素,此时的总元素个数如果大于了数组elementData的长度才会扩容
//也就是说如果elementData的长度为10,那么只有在准备插入11的元素的时候才会扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}

/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
//扩容操作
//每次扩容为原集合容量的1.5倍: newCapacity = oldCapacity + (oldCapacity >> 1)
private void grow(int minCapacity) { //minCapacity=10
// overflow-conscious code
int oldCapacity = elementData.length; //oldCapacity=0
int newCapacity = oldCapacity + (oldCapacity >> 1); //newCapacity=0+(0>>1)=0
if (newCapacity - minCapacity < 0) //0-10=-10
newCapacity = minCapacity; //newCapacity=10
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); //进行数组拷贝,此时的newCapacity=10
}

Vector

Vector和ArrayList一样,也是基于数组实现的。在实例化对象时就初始化内部数组(大小为10) ,且Vector支持线程同步,即在多线程的环境下,能保证同一时间只有一个线程能够操控Vector,保证了数据的一致性【采用synchronized修饰增删改查方法】。但正是由于Vector保证了线程同步(锁的粒度太粗,将当前集合对象锁住,读读都互斥),所以导致了其性能较ArrayList低很多。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/**
* The array buffer into which the components of the vector are
* stored. The capacity of the vector is the length of this array buffer,
* and is at least large enough to contain all the vector's elements.
*
* <p>Any array elements following the last element in the Vector are null.
*
* @serial
*/
//vector的元素也是存放在数组中的
protected Object[] elementData;

/**
* Constructs an empty vector so that its internal data array
* has size {@code 10} and its standard capacity increment is
* zero.
*/
//首先初始化一个容量为10的数组
public Vector() {
this(10);
}

/**
* Appends the specified element to the end of this Vector.
*
* @param e element to be appended to this Vector
* @return {@code true} (as specified by {@link Collection#add})
* @since 1.2
*/
//synchronized修饰方法,向集合尾部添加元素
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}

/**
* This implements the unsynchronized semantics of ensureCapacity.
* Synchronized methods in this class can internally call this
* method for ensuring capacity without incurring the cost of an
* extra synchronization.
*
* @see #ensureCapacity(int)
*/
//加上正准备插入的这个元素,此时的总元素个数如果大于了数组elementData的长度才会扩容
//也就是说如果elementData的长度为10,那么只有在准备插入11的元素的时候才会扩容
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}

//扩容操作
//扩容为原集合容量的2倍
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity); //进行数组拷贝,此时的newCapacity=10
}

/**
* Replaces the element at the specified position in this Vector with the
* specified element.
*
* @param index index of the element to replace
* @param element element to be stored at the specified position
* @return the element previously at the specified position
* @throws ArrayIndexOutOfBoundsException if the index is out of range
* ({@code index < 0 || index >= size()})
* @since 1.2
*/
public synchronized E set(int index, E element) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);

E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}



/**
* Returns the element at the specified position in this Vector.
*
* @param index index of the element to return
* @return object at the specified index
* @throws ArrayIndexOutOfBoundsException if the index is out of range
* ({@code index < 0 || index >= size()})
* @since 1.2
*/
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);

return elementData(index);
}

/**
* Removes the element at the specified position in this Vector.
* Shifts any subsequent elements to the left (subtracts one from their
* indices). Returns the element that was removed from the Vector.
*
* @throws ArrayIndexOutOfBoundsException if the index is out of range
* ({@code index < 0 || index >= size()})
* @param index the index of the element to be removed
* @return element that was removed
* @since 1.2
*/
public synchronized E remove(int index) {
modCount++;
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
E oldValue = elementData(index);

int numMoved = elementCount - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--elementCount] = null; // Let gc do its work

return oldValue;
}

LinkedList

LinkedList是基于双向链表实现的,它支持对数据的随机插入和删除。但是如果要访问元素的话就比较慢了,它必须从头遍历。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* Pointer to first node.
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
transient Node<E> first;

/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
transient Node<E> last;

/**
* Constructs an empty list.
*/
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;
}
}

区别

  • 从初始化、扩容、线程安全三方面对ArrayList与Vector

ArrayList采用懒加载策略(第一次add时才初始化内部数组,默认初始化大小为10) 扩容为原先数组大小的1.5倍。采用异步处理,线程不安全,性能较高 ArrayList在大部分场合(80%,频繁查找、在集合末端插入与删除)都采用ArrayList

Vector在实例化对象时就初始化内部数组(大小为10),capacityIncrement默认为0,扩容为原先数组大小的2倍。采用synchronized修饰增删改查方法,线程安全,性能较低(锁的粒度太粗,将当前集合对象锁住,读读都互斥),即使要用性能安全的集合,也不推荐使用Vector

  • LinkedList采用异步处理,线程不安全 频繁在任意位置的插入与删除考虑使用LinkedList LinkedList是Queue接口的常用子类

使用场景

如果你的程序更多的是进行元素的查找或者从集合末端插入或删除元素,建议使用ArrayList;如果更多的是进行随机插入和删除操作,LinkedList会更优。