一、概述
1、介紹
ArrayList元素是有序的,可重復。線程不安全的。底層維護一個 Object 數組。
JDK1.7:ArrayList像餓漢式,默認初始長度直接創建一個容量為 10 的數組。
JDK1.8:ArrayList像懶漢式,默認一開始創建一個長度為 0 的數組,當添加第一個元素時再創建一個始容量為10的數組。
默認情況擴容都為原來數組的 1.5 倍。
繼承關系:

擴容原理:

2、遍歷方式
①隨機訪問
由於實現了 RandomAccess 接口,它支持通過索引值去隨機訪問元素。
1 ArrayList<String> list = new ArrayList<>(); 2 list.add("1"); 3 list.add("3"); 4 list.add("2"); 5 for (int i = 0; i < list.size(); i++) { 6 System.out.println(list.get(i)); 7 }
②增強forEach,底層還是使用的迭代器
1 ArrayList<String> list = new ArrayList<>(); 2 list.add("1"); 3 list.add("3"); 4 list.add("2"); 5 for (String s : list) { 6 System.out.println(s); 7 }
這種語法可以看成是 JDK 的一種語法糖,通過反編譯 class 文件,可以看到生成的 java 文件,還是通過調用 Iterator 迭代器來遍歷的。
1 for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) { 2 iterator.next(); 3 }
③迭代器 iterator
1 ArrayList<String> list = new ArrayList<>(); 2 list.add("1"); 3 list.add("3"); 4 list.add("2"); 5 6 final Iterator<String> iterator = list.iterator(); 7 while (iterator.hasNext()) { 8 System.out.println(iterator.next()); 9 }
④迭代器 ListIterator
1 ArrayList<String> list = new ArrayList<>(); 2 list.add("1"); 3 list.add("3"); 4 list.add("2"); 5 6 final ListIterator<String> iterator = list.listIterator(); 7 // 向后遍歷 8 while (iterator.hasNext()) { 9 System.out.println(iterator.next()); // 1 3 2 10 } 11 12 // 向前遍歷.此時由於上面進行了向后遍歷,游標指向了最后一個元素,所以此處向前遍歷能有值 13 while (iterator.hasPrevious()) { 14 System.out.println(iterator.previous()); // 2 3 1 15 }
二、類源碼
1、類聲明
源碼示例:
1 * @author Josh Bloch 2 * @author Neal Gafter 3 * @see Collection 4 * @see List 5 * @see LinkedList 6 * @see Vector 7 * @since 1.2 8 */ 9 10 public class ArrayList<E> extends AbstractList<E> 11 implements List<E>, RandomAccess, Cloneable, java.io.Serializable 12 {}
繼承了AbstractList:AbstractList提供List接口的骨干實現,以最大限度地減少"隨機訪問"數據存儲(如ArrayList)實現Llist所需的工作。
實現了 List 接口:定義了實現該接口的類都必須要實現的一組方法,實現了所有可選列表操作。
實現了 RandmoAccess 接口:表明ArrayList支持快速(通常是固定時間)隨機訪問。此接口的主要目的是允許一般的算法更改其行為,從而在將其應用到隨機或連續訪問列表時能提供良好的性能。比如在工具類 Collections 中,應用二分查找方法時判斷是否實現了 RandomAccess 接口:
1 // Collections類 2 public static <T> 3 int binarySearch(List<? extends Comparable<? super T>> list, T key) { 4 if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD) 5 return Collections.indexedBinarySearch(list, key); 6 else 7 return Collections.iteratorBinarySearch(list, key); 8 }
實現了 Cloneable 接口:一個標記接口,接口內無任何方法體和常量的聲明。需要復寫Object.clone()函數,表示它可以被克隆。
實現了 Serializable 接口:一個標記接口,標識該類可序列化。
2、類屬性
源碼示例:讀一下源碼中的英文注釋。
1 private static final long serialVersionUID = 8683452581122892189L; 2 3 // 默認的初始化容量(大小) 4 /** 5 * Default initial capacity. 6 */ 7 private static final int DEFAULT_CAPACITY = 10; 8 9 // 空的數組實例 10 /** 11 * Shared empty array instance used for empty instances. 12 */ 13 private static final Object[] EMPTY_ELEMENTDATA = {}; 14 15 // 也是一個空的數組實例.個人覺得和上一個屬性沒啥區別 16 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; 17 18 // 任何一個空的 elementData 就是 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 19 // 當它第一次 add 元素的時候會被擴展到DEFAULT_CAPACITY,也就是 10 20 /** 21 * The array buffer into which the elements of the ArrayList are stored. 22 * The capacity of the ArrayList is the length of this array buffer. Any 23 * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 24 * will be expanded to DEFAULT_CAPACITY when the first element is added. 25 */ 26 transient Object[] elementData; // non-private to simplify nested class access 27 28 // 容器的實際元素個數 29 private int size; 30 31 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
3、類構造器
源碼示例:三個重載(重要)
ArrayList list = new ArrayList(); // 集合長度初始化是0,而不是 10,JDK7才是10。
1 public ArrayList() { 2 // 很清楚的看到,初始化的是一個空數組 3 this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; 4 }
ArrayList list = new ArrayList(7); // 指定大小時,初始多大就是多大。
1 // 不寫注釋也能看懂的代碼 2 public ArrayList(int initialCapacity) { 3 if (initialCapacity > 0) { 4 // 大於 0 時, new 的數組大小就是多少 5 this.elementData = new Object[initialCapacity]; 6 } else if (initialCapacity == 0) { 7 // 等於 0 時, 初始化的是一個空數組 8 this.elementData = EMPTY_ELEMENTDATA; 9 } else { 10 // 否則,參數異常 11 throw new IllegalArgumentException("Illegal Capacity: "+ 12 initialCapacity); 13 } 14 }
傳入一個集合。
1 public ArrayList(Collection<? extends E> c) { 2 elementData = c.toArray(); 3 if ((size = elementData.length) != 0) { 4 // c.toArray might (incorrectly) not return Object[] (see 6260652) 5 if (elementData.getClass() != Object[].class) 6 // 數組拷貝 7 elementData = Arrays.copyOf(elementData, size, Object[].class); 8 } else { 9 // 否則,就初始化一個空數組 10 // replace with empty array. 11 this.elementData = EMPTY_ELEMENTDATA; 12 } 13 }
4、add()方法
源碼示例:擴容原理
1 public boolean add(E e) { 2 // 判斷是否需要擴容 3 ensureCapacityInternal(size + 1); // Increments modCount!! 4 elementData[size++] = e; 5 return true; 6 } 7 8 private void ensureCapacityInternal(int minCapacity) { 9 // 如果是第一次初始化的空數組 10 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { 11 // minCapacity = max(10,1) 體現出第一次 add 時,才初始化長度 10 的數組 12 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); 13 } 14 15 ensureExplicitCapacity(minCapacity); 16 } 17 18 private void ensureExplicitCapacity(int minCapacity) { 19 // 修改次數+1.用於集合的快速失敗機制 20 modCount++; 21 22 // 需要擴容 23 // overflow-conscious code 24 if (minCapacity - elementData.length > 0) 25 grow(minCapacity); 26 }
源碼示例:真正擴容的方法
1 private void grow(int minCapacity) { 2 // overflow-conscious code 3 int oldCapacity = elementData.length; 4 5 // 體現出擴容為原來的 1.5 倍 6 int newCapacity = oldCapacity + (oldCapacity >> 1); 7 8 // 當新數組長度仍然比minCapacity小,則為保證最小長度,新數組等於minCapacity 9 if (newCapacity - minCapacity < 0) 10 newCapacity = minCapacity; 11 12 // MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8 = 2147483639 13 if (newCapacity - MAX_ARRAY_SIZE > 0) 14 newCapacity = hugeCapacity(minCapacity); 15 16 // 將原數組拷貝到一個大小為 newCapacity 的新數組(注意是拷貝引用) 17 // minCapacity is usually close to size, so this is a win: 18 elementData = Arrays.copyOf(elementData, newCapacity); 19 } 20 21 // 用於判斷容量是否超過最大值 22 private static int hugeCapacity(int minCapacity) { 23 if (minCapacity < 0) // overflow 24 throw new OutOfMemoryError(); 25 return (minCapacity > MAX_ARRAY_SIZE) ? 26 Integer.MAX_VALUE : 27 MAX_ARRAY_SIZE; 28 }
說明:
modCount給ArrayList的迭代器使用的,在並發修改時,提供快速失敗行為,保證modCount在迭代期間不變,否則拋出ConcurrentModificationException異常。
ArrayList 的擴容,核心方法就是調用 Arrays.copyOf 方法,創建一個更大的數組,然后將原數組元素拷貝過去。
總結:
①當通過 ArrayList() 構造一個空集合,初始長度是為0的,第1次添加元素,會創建一個長度為10的數組,並將該元素賦值到數組的第一個位置。
②第2次添加元素,集合不為空,由於集合大小size + 1 < 數組的長度 10,所以直接添加元素到數組的第二個位置,不用擴容。
③第 11 次添加元素,此時 size+1 = 11 > 數組長度10。這時候創建一個長度為10+10*0.5 = 15 的數組(擴容1.5倍),然后將原數組元素引用拷貝到新數組。並將第 11 次添加的元素賦值到新數組下標為10的位置。
④第 Integer.MAX_VALUE - 8 = 2147483639,然后 2147483639%1.5=1431655759(這個數是要進行擴容)次添加元素,為了防止溢出,此時會直接創建一個 1431655759 + 1 大小的數組,這樣一直,每次添加一個元素,都只擴大一個范圍。
⑤第 Integer.MAX_VALUE - 7 次添加元素時,創建一個大小為 Integer.MAX_VALUE 的數組,在進行元素添加。
⑥第 Integer.MAX_VALUE + 1 次添加元素時,拋出 OutOfMemoryError 異常。
5、remove()方法
源碼示例:
1 // 根據索引刪除 2 public E remove(int index) { 3 // 檢查索引 index 是否越界 4 rangeCheck(index); 5 6 modCount++; 7 // 獲取索引處的元素 8 E oldValue = elementData(index); 9 10 int numMoved = size - index - 1; 11 // 表示 0 <= index < (size-1),即索引不是最后一個元素 12 if (numMoved > 0) 13 // 將數組elementData的下標index+1之后長度為numMoved的元素拷貝到從index開始的位置 14 System.arraycopy(elementData, index+1, elementData, index, 15 numMoved); 16 // 將數組最后一個元素置為 null, 便於垃圾回收 17 elementData[--size] = null; // clear to let GC do its work 18 19 return oldValue; 20 } 21 22 // 刪除指定元素 23 public boolean remove(Object o) { 24 // 如果 o 為null 25 if (o == null) { 26 for (int index = 0; index < size; index++) 27 if (elementData[index] == null) { 28 fastRemove(index); 29 return true; 30 } 31 } else { 32 // 如果o不是null,則循環查找.刪除第一次出現的該元素 33 for (int index = 0; index < size; index++) 34 if (o.equals(elementData[index])) { 35 fastRemove(index); 36 return true; 37 } 38 } 39 return false; 40 } 41 private void fastRemove(int index) { 42 modCount++; 43 int numMoved = size - index - 1; 44 if (numMoved > 0) 45 // 通過System.arraycopy進行數組自身拷貝。 46 System.arraycopy(elementData, index+1, elementData, index, 47 numMoved); 48 elementData[--size] = null; // clear to let GC do its work 49 }
6、set()方法
源碼示例:將指定索引index處的元素修改。
1 public E set(int index, E element) { 2 rangeCheck(index); 3 4 // 獲取索引處的元素 5 E oldValue = elementData(index); 6 // 將index處元素修改為element 7 elementData[index] = element; 8 return oldValue; 9 } 10 11 // 很簡單.就是檢查索引合法性 12 private void rangeCheck(int index) { 13 if (index >= size) 14 throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); 15 }
7、get()方法
源碼示例:
1 public E get(int index) { 2 rangeCheck(index); 3 // 通過索引下標返回數組中的元素 4 return elementData(index); 5 }
源碼示例:很簡單,略
indexOf(Object o):返回集合中第一次出現該元素的下標,沒有則返回 -1。
lastIndexOf(Object o):返回集合中最后一次出現該元素的下標。沒有則返回 -1。
8、SubList()
返回從 [fromIndex, toIndex) 的一個子串。但是注意,這里只是原集合的一個視圖。
1 public class Main { 2 public static void main(String[] args) { 3 ArrayList<String> list = new ArrayList<>(); 4 list.add("1"); 5 list.add("3"); 6 list.add("2"); 7 8 List<String> subList = list.subList(0, 1); 9 System.out.println(subList); // [1] 10 11 subList.add("k"); 12 System.out.println(subList); // [1, k] 13 14 // 對子串的添加影響了原集合 15 System.out.println(list); // [1, k, 3, 2] 16 } 17 }
想要獨立出來一個集合,解決辦法如下:
1 List<String> sub = new ArrayList<>(list.subList(0, 1));
9、Size()
1 public int size() { 2 // 返回集合的大小 3 return size; 4 }
10、isEmpty()
1 public boolean isEmpty() { 2 // 集合是否為空 3 return size == 0; 4 }
11、trimToSize()
1 public void trimToSize() { 2 modCount++; 3 if (size < elementData.length) { 4 // 如果集合大小為 0 ,則數組為空 5 elementData = (size == 0) 6 ? EMPTY_ELEMENTDATA 7 8 // 否則,按集合實際大小拷貝一份數組 9 : Arrays.copyOf(elementData, size); 10 } 11 }
該方法用於回收多余的內存。可以在確定集合不在添加多余的元素之后,調用 trimToSize() 方法會將數組大小調整為集合元素的大小。
注意:該方法會花時間來復制數組元素,所以應該在確定不會添加元素之后在調用。
三、迭代器
1、iterator
源碼示例:
1 // 返回一個 Itr 對象.這個類是 ArrayList 的內部類。 2 public Iterator<E> iterator() { 3 return new Itr(); 4 } 5 6 private class Itr implements Iterator<E> { 7 // 游標.下一個要返回的元素的索引 8 int cursor; // index of next element to return 9 // 返回最后一個元素的索引; 如果沒有返回-1. 10 int lastRet = -1; // index of last element returned; -1 if no such 11 int expectedModCount = modCount; 12 13 // 通過 cursor != size 判斷是否還有下一個元素 14 public boolean hasNext() { 15 return cursor != size; 16 } 17 18 @SuppressWarnings("unchecked") 19 public E next() { 20 // 迭代器進行元素迭代時同時進行增加和刪除操作,會拋出異常 21 checkForComodification(); 22 int i = cursor; 23 if (i >= size) 24 throw new NoSuchElementException(); 25 Object[] elementData = ArrayList.this.elementData; 26 if (i >= elementData.length) 27 throw new ConcurrentModificationException(); 28 // 游標向后移動一位 29 cursor = i + 1; 30 // 返回索引為 i 處的元素,並將lastRet賦值為i 31 return (E) elementData[lastRet = i]; 32 } 33 34 public void remove() { 35 if (lastRet < 0) 36 throw new IllegalStateException(); 37 checkForComodification(); 38 39 try { 40 // 調用ArrayList的remove方法刪除元素 41 ArrayList.this.remove(lastRet); 42 // 游標指向刪除元素的位置,本來是lastRet+1的,這里刪除一個元素,然后游標就不變了 43 cursor = lastRet; 44 // 恢復默認值-1 45 lastRet = -1; 46 //expectedModCount值和modCount同步,因為進行add和remove操作,modCount會加1 47 expectedModCount = modCount; 48 } catch (IndexOutOfBoundsException ex) { 49 throw new ConcurrentModificationException(); 50 } 51 } 52 53 @Override 54 @SuppressWarnings("unchecked") 55 public void forEachRemaining(Consumer<? super E> consumer) { 56 Objects.requireNonNull(consumer); 57 final int size = ArrayList.this.size; 58 int i = cursor; 59 if (i >= size) { 60 return; 61 } 62 final Object[] elementData = ArrayList.this.elementData; 63 if (i >= elementData.length) { 64 throw new ConcurrentModificationException(); 65 } 66 while (i != size && modCount == expectedModCount) { 67 consumer.accept((E) elementData[i++]); 68 } 69 // update once at end of iteration to reduce heap write traffic 70 cursor = i; 71 lastRet = i - 1; 72 checkForComodification(); 73 } 74 75 76 // 在新增元素add() 和 刪除元素 remove() 時, modCount++。修改set() 是沒有的 77 // 也就是說不能在迭代器進行元素迭代時進行增加和刪除操作,否則拋出異常 78 final void checkForComodification() { 79 if (modCount != expectedModCount) 80 throw new ConcurrentModificationException(); 81 } 82 }
注意:在進行 next() 方法調用的時候,會進行 checkForComodification() 調用,該方法表示迭代器進行元素迭代時,如果同時進行增加和刪除操作,會拋出 ConcurrentModificationException 異常。比如:
1 ArrayList<String> list = new ArrayList<>(); 2 list.add("1"); 3 list.add("3"); 4 list.add("2"); 5 6 final Iterator<String> iterator = list.iterator(); 7 while (iterator.hasNext()) { 8 final String next = iterator.next(); 9 10 // Exception in thread "main" java.util.ConcurrentModificationException 11 // list.remove(next); 12 list.set(0, "k"); 13 14 // 正確方式是調用迭代器的 remove 方法 15 iterator.remove(); 16 }
注意:迭代器只能向后遍歷,不能向前遍歷,能夠刪除元素,但是不能新增元素。
2、listIterator
這是 list 集合特有的迭代器。可以一邊遍歷,一邊新增或刪除。
1 public class Main { 2 public static void main(String[] args) { 3 ArrayList<String> list = new ArrayList<>(); 4 list.add("1"); 5 list.add("3"); 6 list.add("2"); 7 8 final ListIterator<String> iterator = list.listIterator(); 9 // 向后遍歷 10 while (iterator.hasNext()) { 11 System.out.print(iterator.next() + " "); // 1 3 2 12 13 iterator.add("K"); 14 } 15 16 // [1, K, 3, K, 2, K] 17 System.out.println("\n" + list); 18 19 // 向前遍歷 20 while (iterator.hasPrevious()) { 21 System.out.print(iterator.previous() + " "); // K 2 K 3 K 1 22 } 23 } 24 }
相比於 Iterator 迭代器, listIterator 多出了能向前迭代,以及新增元素的功能。
源碼示例:
1 public interface ListIterator<E> extends Iterator<E> { 2 // 正向迭代.是否有下一個 3 boolean hasNext(); 4 // 取下一個 5 E next(); 6 // 反向迭代.是否有上一個 7 boolean hasPrevious(); 8 // 取上一個 9 E previous(); 10 int nextIndex(); 11 int previousIndex(); 12 // 刪除元素 13 void remove(); 14 void set(E e); 15 // 添加元素 16 void add(E e); 17 }
在ArrayList類中,對上述接口的實現。
1 public ListIterator<E> listIterator() { 2 return new ListItr(0); 3 } 4 5 // 一個內部類 6 private class ListItr extends Itr implements ListIterator<E> { 7 // 進行游標初始化.默認是 0 8 ListItr(int index) { 9 super(); 10 cursor = index; 11 } 12 // 是否有上一個元素 13 public boolean hasPrevious() { 14 return cursor != 0; 15 } 16 // 返回下一個元素的索引 17 public int nextIndex() { 18 return cursor; 19 } 20 // 返回上一個元素的索引 21 public int previousIndex() { 22 return cursor - 1; 23 } 24 25 @SuppressWarnings("unchecked") 26 public E previous() { 27 // 迭代器進行元素迭代時同時進行增加和刪除操作,會拋出異常 28 checkForComodification(); 29 int i = cursor - 1; 30 if (i < 0) 31 throw new NoSuchElementException(); 32 Object[] elementData = ArrayList.this.elementData; 33 if (i >= elementData.length) 34 throw new ConcurrentModificationException(); 35 // 游標指向上一個元素 36 cursor = i; 37 // 返回上一個元素的值 38 return (E) elementData[lastRet = i]; 39 } 40 41 public void set(E e) { 42 if (lastRet < 0) 43 throw new IllegalStateException(); 44 checkForComodification(); 45 46 try { 47 ArrayList.this.set(lastRet, e); 48 } catch (IndexOutOfBoundsException ex) { 49 throw new ConcurrentModificationException(); 50 } 51 } 52 53 public void add(E e) { 54 checkForComodification(); 55 56 try { 57 int i = cursor; 58 ArrayList.this.add(i, e); 59 cursor = i + 1; 60 lastRet = -1; 61 expectedModCount = modCount; 62 } catch (IndexOutOfBoundsException ex) { 63 throw new ConcurrentModificationException(); 64 } 65 } 66 }
參考文檔:https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html#
