ArrayList源碼剖析

ArrayList簡介

ArrayList是基於數組實現的,是一個動態數組,其容量能自動增長,類似於C語言中的動態申請內存,動態增長內存。

ArrayList不是線程安全的,只能用在單線程環境下,多線程環境下可以考慮用Collections.synchronizedList(List l)函數返回一個線程安全的ArrayList類,也可以使用concurrent併發包下的CopyOnWriteArrayList類。

ArrayList實現了Serializable接口,因此它支持序列化,能夠通過序列化傳輸,實現了RandomAccess接口,支持快速隨機訪問,實際上就是通過下標序號進行快速訪問,實現了Cloneable接口,能被克隆。

ArrayList源碼剖析

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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
package java.util; 
 
public class ArrayList<E> extends AbstractList<E> 
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable 
    // 序列版本號 
    private static final long serialVersionUID = 8683452581122892189L; 
 
    // ArrayList基於該數組實現,用該數組保存數據
    private transient Object[] elementData; 
 
    // ArrayList中實際數據的數量 
    private int size; 
 
    // ArrayList帶容量大小的構造函數。 
    public ArrayList(int initialCapacity) { 
        super(); 
        if (initialCapacity < 0
            throw new IllegalArgumentException("Illegal Capacity: "
                                               initialCapacity); 
        // 新建一個數組 
        this.elementData = new Object[initialCapacity]; 
    
 
    // ArrayList無參構造函數。默認容量是10。 
    public ArrayList() { 
        this(10); 
    
 
    // 創建一個包含collection的ArrayList 
    public ArrayList(Collection<? extends E> c) { 
        elementData = c.toArray(); 
        size = elementData.length; 
        if (elementData.getClass() != Object[].class
            elementData = Arrays.copyOf(elementData, size, Object[].class); 
    
 
    // 將當前容量值設爲實際元素個數 
    public void trimToSize() { 
        modCount++; 
        int oldCapacity = elementData.length; 
        if (size < oldCapacity) { 
            elementData = Arrays.copyOf(elementData, size); 
        
    
 
    // 確定ArrarList的容量。 
    // 若ArrayList的容量不足以容納當前的全部元素,設置 新的容量=“(原始容量x3)/2 + 1” 
    public void ensureCapacity(int minCapacity) { 
        // 將“修改統計數”+1,該變量主要是用來實現fail-fast機制的 
        modCount++; 
        int oldCapacity = elementData.length; 
        // 若當前容量不足以容納當前的元素個數,設置 新的容量=“(原始容量x3)/2 + 1” 
        if (minCapacity > oldCapacity) { 
            Object oldData[] = elementData; 
            int newCapacity = (oldCapacity * 3)/2 + 1
            //如果還不夠,則直接將minCapacity設置爲當前容量
            if (newCapacity < minCapacity) 
                newCapacity = minCapacity; 
            elementData = Arrays.copyOf(elementData, newCapacity); 
        
    
 
    // 添加元素e 
    public boolean add(E e) { 
        // 確定ArrayList的容量大小 
        ensureCapacity(size + 1);  // Increments modCount!! 
        // 添加e到ArrayList中 
        elementData[size++] = e; 
        return true
    
 
    // 返回ArrayList的實際大小 
    public int size() { 
        return size; 
    
 
    // ArrayList是否包含Object(o) 
    public boolean contains(Object o) { 
        return indexOf(o) >= 0
    
 
    //返回ArrayList是否爲空 
    public boolean isEmpty() { 
        return size == 0
    
 
    // 正向查找,返回元素的索引值 
    public int indexOf(Object o) { 
        if (o == null) { 
            for (int i = 0; i < size; i++) 
            if (elementData[i]==null
                return i; 
            } else
                for (int i = 0; i < size; i++) 
                if (o.equals(elementData[i])) 
                    return i; 
            
            return -1
        
 
        // 反向查找,返回元素的索引值 
        public int lastIndexOf(Object o) { 
        if (o == null) { 
            for (int i = size-1; i >= 0; i--) 
            if (elementData[i]==null
                return i; 
        } else
            for (int i = size-1; i >= 0; i--) 
            if (o.equals(elementData[i])) 
                return i; 
        
        return -1
    
 
    // 反向查找(從數組末尾向開始查找),返回元素(o)的索引值 
    public int lastIndexOf(Object o) { 
        if (o == null) { 
            for (int i = size-1; i >= 0; i--) 
            if (elementData[i]==null
                return i; 
        } else
            for (int i = size-1; i >= 0; i--) 
            if (o.equals(elementData[i])) 
                return i; 
        
        return -1
    
 
    // 返回ArrayList的Object數組 
    public Object[] toArray() { 
        return Arrays.copyOf(elementData, size); 
    
 
    // 返回ArrayList元素組成的數組
    public <T> T[] toArray(T[] a) { 
        // 若數組a的大小 < ArrayList的元素個數; 
        // 則新建一個T[]數組,數組大小是“ArrayList的元素個數”,並將“ArrayList”全部拷貝到新數組中 
        if (a.length < size) 
            return (T[]) Arrays.copyOf(elementData, size, a.getClass()); 
 
        // 若數組a的大小 >= ArrayList的元素個數; 
        // 則將ArrayList的全部元素都拷貝到數組a中。 
        System.arraycopy(elementData, 0, a, 0, size); 
        if (a.length > size) 
            a[size] = null
        return a; 
    
 
    // 獲取index位置的元素值 
    public E get(int index) { 
        RangeCheck(index); 
 
        return (E) elementData[index]; 
    
 
    // 設置index位置的值爲element 
    public E set(int index, E element) { 
        RangeCheck(index); 
 
        E oldValue = (E) elementData[index]; 
        elementData[index] = element; 
        return oldValue; 
    
 
    // 將e添加到ArrayList中 
    public boolean add(E e) { 
        ensureCapacity(size + 1);  // Increments modCount!! 
        elementData[size++] = e; 
        return true
    
 
    // 將e添加到ArrayList的指定位置 
    public void add(int index, E element) { 
        if (index > size || index < 0
            throw new IndexOutOfBoundsException( 
            "Index: "+index+", Size: "+size); 
 
        ensureCapacity(size+1);  // Increments modCount!! 
        System.arraycopy(elementData, index, elementData, index + 1
             size - index); 
        elementData[index] = element; 
        size++; 
    
 
    // 刪除ArrayList指定位置的元素 
    public E remove(int index) { 
        RangeCheck(index); 
 
        modCount++; 
        E oldValue = (E) elementData[index]; 
 
        int numMoved = size - index - 1
        if (numMoved > 0
            System.arraycopy(elementData, index+1, elementData, index, 
                 numMoved); 
        elementData[--size] = null; // Let gc do its work 
 
        return oldValue; 
    
 
    // 刪除ArrayList的指定元素 
    public boolean remove(Object o) { 
        if (o == null) { 
                for (int index = 0; index < size; index++) 
            if (elementData[index] == null) { 
                fastRemove(index); 
                return true
            
        } else
            for (int index = 0; index < size; index++) 
            if (o.equals(elementData[index])) { 
                fastRemove(index); 
                return true
            
        
        return false
    
 
    // 快速刪除第index個元素 
    private void fastRemove(int index) { 
        modCount++; 
        int numMoved = size - index - 1
        // 從"index+1"開始,用後面的元素替換前面的元素。 
        if (numMoved > 0
            System.arraycopy(elementData, index+1, elementData, index, 
                             numMoved); 
        // 將最後一個元素設爲null 
        elementData[--size] = null; // Let gc do its work 
    
 
    // 刪除元素 
    public boolean remove(Object o) { 
        if (o == null) { 
            for (int index = 0; index < size; index++) 
            if (elementData[index] == null) { 
                fastRemove(index); 
            return true
            
        } else
            // 便利ArrayList,找到“元素o”,則刪除,並返回true。 
            for (int index = 0; index < size; index++) 
            if (o.equals(elementData[index])) { 
                fastRemove(index); 
            return true
            
        
        return false
    
 
    // 清空ArrayList,將全部的元素設爲null 
    public void clear() { 
        modCount++; 
 
        for (int i = 0; i < size; i++) 
            elementData[i] = null
 
        size = 0
    
 
    // 將集合c追加到ArrayList中 
    public boolean addAll(Collection<? extends E> c) { 
        Object[] a = c.toArray(); 
        int numNew = a.length; 
        ensureCapacity(size + numNew);  // Increments modCount 
        System.arraycopy(a, 0, elementData, size, numNew); 
        size += numNew; 
        return numNew != 0
    
 
    // 從index位置開始,將集合c添加到ArrayList 
    public boolean addAll(int index, Collection<? extends E> c) { 
        if (index > size || index < 0
            throw new IndexOutOfBoundsException( 
            "Index: " + index + ", Size: " + size); 
 
        Object[] a = c.toArray(); 
        int numNew = a.length; 
        ensureCapacity(size + numNew);  // Increments modCount 
 
        int numMoved = size - index; 
        if (numMoved > 0
            System.arraycopy(elementData, index, elementData, index + numNew, 
                 numMoved); 
 
        System.arraycopy(a, 0, elementData, index, numNew); 
        size += numNew; 
        return numNew != 0
    
 
    // 刪除fromIndex到toIndex之間的全部元素。 
    protected void removeRange(int fromIndex, int toIndex) { 
    modCount++; 
    int numMoved = size - toIndex; 
        System.arraycopy(elementData, toIndex, elementData, fromIndex, 
                         numMoved); 
 
    // Let gc do its work 
    int newSize = size - (toIndex-fromIndex); 
    while (size != newSize) 
        elementData[--size] = null
    
 
    private void RangeCheck(int index) { 
    if (index >= size) 
        throw new IndexOutOfBoundsException( 
        "Index: "+index+", Size: "+size); 
    
 
    // 克隆函數 
    public Object clone() { 
        try
            ArrayList<E> v = (ArrayList<E>) super.clone(); 
            // 將當前ArrayList的全部元素拷貝到v中 
            v.elementData = Arrays.copyOf(elementData, size); 
            v.modCount = 0
            return v; 
        } catch (CloneNotSupportedException e) { 
            // this shouldn't happen, since we are Cloneable 
            throw new InternalError(); 
        
    
 
    // java.io.Serializable的寫入函數 
    // 將ArrayList的“容量,所有的元素值”都寫入到輸出流中 
    private void writeObject(java.io.ObjectOutputStream s) 
        throws java.io.IOException{ 
    // Write out element count, and any hidden stuff 
    int expectedModCount = modCount; 
    s.defaultWriteObject(); 
 
        // 寫入“數組的容量” 
        s.writeInt(elementData.length); 
 
    // 寫入“數組的每一個元素” 
    for (int i=0; i<size; i++) 
            s.writeObject(elementData[i]); 
 
    if (modCount != expectedModCount) { 
            throw new ConcurrentModificationException(); 
        
 
    
 
    // java.io.Serializable的讀取函數:根據寫入方式讀出 
    // 先將ArrayList的“容量”讀出,然後將“所有的元素值”讀出 
    private void readObject(java.io.ObjectInputStream s) 
        throws java.io.IOException, ClassNotFoundException { 
        // Read in size, and any hidden stuff 
        s.defaultReadObject(); 
 
        // 從輸入流中讀取ArrayList的“容量” 
        int arrayLength = s.readInt(); 
        Object[] a = elementData = new Object[arrayLength]; 
 
        // 從輸入流中將“所有的元素值”讀出 
        for (int i=0; i<size; i++) 
            a[i] = s.readObject(); 
    }  

幾點總結

關於ArrayList的源碼,給出幾點比較重要的總結:

1、注意其三個不同的構造方法。無參構造方法構造的ArrayList的容量默認爲10,帶有Collection參數的構造方法,將Collection轉化爲數組賦給ArrayList的實現數組elementData。

2、注意擴充容量的方法ensureCapacity。ArrayList在每次增加元素(可能是1個,也可能是一組)時,都要調用該方法來確保足夠的容量。當容量不足以容納當前的元素個數時,就設置新的容量爲舊的容量的1.5倍加1,如果設置後的新容量還不夠,則直接新容量設置爲傳入的參數(也就是所需的容量),而後用Arrays.copyof()方法將元素拷貝到新的數組(詳見下面的第3點)。從中可以看出,當容量不夠時,每次增加元素,都要將原來的元素拷貝到一個新的數組中,非常之耗時,也因此建議在事先能確定元素數量的情況下,才使用ArrayList,否則建議使用LinkedList。

3、ArrayList的實現中大量地調用了Arrays.copyof()和System.arraycopy()方法。我們有必要對這兩個方法的實現做下深入的瞭解。

首先來看Arrays.copyof()方法。它有很多個重載的方法,但實現思路都是一樣的,我們來看泛型版本的源碼:

1
2
3
public static <T> T[] copyOf(T[] original, int newLength) {
    return (T[]) copyOf(original, newLength, original.getClass());
}

很明顯調用了另一個copyof方法,該方法有三個參數,最後一個參數指明要轉換的數據的類型,其源碼如下:

1
2
3
4
5
6
7
8
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new Object[newLength]
        : (T[]) Array.newInstance(newType.getComponentType(), newLength);
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}

這裏可以很明顯地看出,該方法實際上是在其內部又創建了一個長度爲newlength的數組,調用System.arraycopy()方法,將原來數組中的元素複製到了新的數組中。

下面來看System.arraycopy()方法。該方法被標記了native,調用了系統的C/C++代碼,在JDK中是看不到的,但在openJDK中可以看到其源碼。該函數實際上最終調用了C語言的memmove()函數,因此它可以保證同一個數組內元素的正確複製和移動,比一般的複製方法的實現效率要高很多,很適合用來批量處理數組。Java強烈推薦在複製大量數組元素時用該方法,以取得更高的效率。

4、注意ArrayList的兩個轉化爲靜態數組的toArray方法。

第一個,Object[] toArray()方法。該方法有可能會拋出java.lang.ClassCastException異常,如果直接用向下轉型的方法,將整個ArrayList集合轉變爲指定類型的Array數組,便會拋出該異常,而如果轉化爲Array數組時不向下轉型,而是將每個元素向下轉型,則不會拋出該異常,顯然對數組中的元素一個個進行向下轉型,效率不高,且不太方便。

第二個,<T> T[] toArray(T[] a)方法。該方法可以直接將ArrayList轉換得到的Array進行整體向下轉型(轉型其實是在該方法的源碼中實現的),且從該方法的源碼中可以看出,參數a的大小不足時,內部會調用Arrays.copyOf方法,該方法內部創建一個新的數組返回,因此對該方法的常用形式如下:

1
2
3
4
public static Integer[] vectorToArray2(ArrayList<Integer> v) { 
    Integer[] newText = (Integer[])v.toArray(new Integer[0]); 
    return newText; 
}

5、ArrayList基於數組實現,可以通過下標索引直接查找到指定位置的元素,因此查找效率高,但每次插入或刪除元素,就要大量地移動元素,插入刪除元素的效率低。

6、在查找給定元素索引值等的方法中,源碼都將該元素的值分爲null和不爲null兩種情況處理,ArrayList中允許元素爲null。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章