JDK1.8源碼(六)——java.util.ArrayList類


一、概述

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 }
ListIterator

  在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#


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM