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。