1. 程式人生 > >Java SE——ArrayList

Java SE——ArrayList

一ArrayList定義
在這裡插入圖片描述
1 實現RandomAccess介面:
這也是一個標記介面,介面中沒有任何方法和常量,表名該類支援快速的隨機訪問。在工具類Collections
中應用二分查詢方法時判斷了是否實現了該介面

1     int binarySearch(List<? extends Comparable<? super T>> list, T key) {
2         if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
3             return Collections.indexedBinarySearch(list, key);
4         else
5             return Collections.iteratorBinarySearch(list, key);
6     }

2 實現Cloneable介面:
淺拷貝可以通過呼叫Object.clone();但是呼叫該方法的物件一定要實現該介面,否則會丟擲CloneNoSupportException。
3 實現了Serializable介面:
表示可以序列化
4 實現list介面:
list介面是list集合類的上層介面,實現該介面的類都必須實現一些方法
在這裡插入圖片描述
二 欄位屬性:

        //集合的預設大小
        private static final int DEFAULT_CAPACITY = 10;
        //空的陣列例項
        private static final Object[] EMPTY_ELEMENTDATA = {};
        //這也是一個空的陣列例項,和EMPTY_ELEMENTDATA空陣列相比是用於瞭解新增元素時陣列膨脹多少
        private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
        //儲存 ArrayList集合的元素,集合的長度即這個陣列的長度
        //1、當 elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 時將會清空 ArrayList
        //2、當新增第一個元素時,elementData 長度會擴充套件為 DEFAULT_CAPACITY=10
        transient Object[] elementData;
        //表示集合的長度
        private int size;

三 建構函式:
在這裡插入圖片描述
  此無參建構函式將建立一個DEFAULTCAPACITY_EMPTY_ELEMENTDATA 宣告的陣列,注意此時初始容量是0,而不是大家以為的 10。
  注意:根據預設建構函式建立的集合,ArrayList list = new ArrayList();此時集合長度是0.
  在這裡插入圖片描述
  建立一個指定大小的陣列,等於0時,就建立一個空陣列,小於0時,丟擲IllegalArgumentException異常。
  在這裡插入圖片描述
 將已有的集合複製到ArrayList中。
 四 方法:
 在這裡插入圖片描述 通過前面的欄位屬性和建構函式,我們知道 ArrayList 集合是由陣列構成的,那麼向 ArrayList 中新增元素,也就是向陣列賦值。我們知道一個數組的宣告是能確定大小的,而使用 ArrayList 時,好像是能新增任意多個元素,這就涉及到陣列的擴容。
  擴容的核心方法就是呼叫Arrays.copyOf 方法,建立一個更大的陣列,然後將原陣列元素拷貝過去即可。下面我們看看具體實現。
  如上圖所示,增加元素之前會呼叫ensureCapacityInternal()方法來確定集合的大小,如果集合滿了,就要擴容操作。

    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
        //這裡的minCapacity 是集合當前的大小+1,可以看add的傳參
    private void ensureCapacityInternal(int minCapacity) {  
        //elementData 是實際用來儲存元素的陣列,注意陣列的大小和集合的大小不是相等的,前面的size是指集合大小
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

接下來就是擴容的方法:

 1     private void grow(int minCapacity) {
 2         int oldCapacity = elementData.length;//得到原始陣列的長度
 3         int newCapacity = oldCapacity + (oldCapacity >> 1);//新陣列的長度等於原陣列長度的1.5倍
 4         if (newCapacity - minCapacity < 0)//當新陣列長度仍然比minCapacity小,則為保證最小長度,新陣列等於minCapacity
 5             newCapacity = minCapacity;
 6         //MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8 = 2147483639
 7         if (newCapacity - MAX_ARRAY_SIZE > 0)//當得到的新陣列長度比 MAX_ARRAY_SIZE 大時,呼叫 hugeCapacity 處理大陣列
 8             newCapacity = hugeCapacity(minCapacity);
 9         //呼叫 Arrays.copyOf 將原陣列拷貝到一個大小為newCapacity的新陣列(注意是拷貝引用)
10         elementData = Arrays.copyOf(elementData, newCapacity);
11     }
12     
13     private static int hugeCapacity(int minCapacity) {
14         if (minCapacity < 0) // 
15             throw new OutOfMemoryError();
16         return (minCapacity > MAX_ARRAY_SIZE) ? //minCapacity > MAX_ARRAY_SIZE,則新陣列大小為Integer.MAX_VALUE
17             Integer.MAX_VALUE :
18             MAX_ARRAY_SIZE;
19     }

對於 ArrayList 集合新增元素,我們總結一下:
  ①、當通過 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 異常。
  注意:能向集合中新增 null 的,因為陣列可以有 null 值存在。
  刪除元素:

  1. 根據給定索引刪除指定元素:
    /**
   * @param index the index of the element to be removed
     * @return the element that was removed from the list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E remove(int index) {
        rangeCheck(index);//判斷給定索引的範圍,超過集合大小則丟擲異常

        modCount++;
        E oldValue = elementData(index);//得到索引處的刪除元素

        int numMoved = size - index - 1;
        if (numMoved > 0) //size-index-1 > 0 表示 0<= index < (size-1),即索引不是最後一個元素
         //通過 System.arraycopy()將陣列elementData 的下標index+1之後長度為 numMoved的元素拷貝到從index開始的位置
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work  將陣列最後一個元素置為 null,便於垃圾回收
 
        return oldValue; // 將被刪除的元素返回
    }

2.根據元素值刪除:

    /**
     * @param o element to be removed from this list, if present
     * @return <tt>true</tt> if this list contained the specified element
     */
    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {  //用if判斷了一次 就直接返回,所以remove是刪除第一次出現的元素
                    fastRemove(index);  
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

修改元素:
修改元素就比較簡單,先呼叫rangeCheck判斷給定索引是否超出範圍,再替換索引出元素,返回被替換的值。

   /**
     * @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 IndexOutOfBoundsException {@inheritDoc}
     */
    public E set(int index, E element) {
        rangeCheck(index);

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

獲得元素:
根據索引獲得元素。
在這裡插入圖片描述
根據元素獲得索引,還有 lastIndexOf(Object o) 方法是返回最後一次出現該元素的下標。

    public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null) //依然是用的if判斷就直接返回,所以也是獲得第一次出現該元素的索引
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;   //如果沒有,就返回-1;
    }