ArrayList源碼分析(二)

這篇文章來自個人博客數組

正文以前

上一篇文章分析了ArrayList的構造器和基本操做的源碼,這一次來說一講ArrayList經常使用方法剩下的源碼(迭代器的留到下次說)bash

主要內容ui

  1. 容器容量
  2. 查找特定元素,包含特定元素,克隆之類的基本用法

正文

開始正題以前,須要先分清楚兩個概念,容器的capacity和size,capacity指的是容器的容量,最多能裝多少,而size指的是當前容器中元素的數量this

接下來直奔正題spa

1. 擴容

  • 設定容量
public void ensureCapacity(int minCapacity) {
        //若是初始時有設定容量,就按設定的來,沒有就按默認的來
        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            // any size if not default element table
            // larger than default for default empty table. It is already
            // supposed to be at default size.
            //: DEFAULT_CAPACITY;
        //若是給定的比上述的要大,就按給定的來
        if (minCapacity > minExpand) {
            //這個方法在下面
            ensureExplicitCapacity(minCapacity);
        }
    }
    //私有方法,用來肯定容量
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        //增長容量
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
複製代碼
  • 增加
/**
     * The maximum size of array to allocate.
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit
     */
    //註解說是由於有的虛擬機要保留數組的前幾位,這個常量的值爲2147483639
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    /**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        //右移一位,至關於除以2
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //二者相比,用更大的一個做爲容量
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //容量特別大時的操做
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        //按照新容量複製數組
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
複製代碼
  • 大容量
private static int hugeCapacity(int minCapacity) {
        //溢出
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
複製代碼

2. 調整大小

若是發現容量多了不少,有點浪費,就能夠調整容器大小到當前大小3d

public void trimToSize() {
        modCount++;
        //若是確實有多餘部分
        if (size < elementData.length) {
            //若是size不爲0,則按照size複製數組
            elementData = (size == 0)
              ? EMPTY_ELEMENTDATA
              : Arrays.copyOf(elementData, size);
        }
    }
複製代碼

3. 列表元素數量

public int size() {
        return size;
    }
複製代碼

4. 列表是否爲空

public boolean isEmpty() {
        return size == 0;
    }
複製代碼

5. 查找列表中特定元素的位置

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;
    }
複製代碼

6. 判斷列表是否包含特定元素

public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }
複製代碼

7. 若是列表中有多個特定元素

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;
    }
複製代碼

8. 克隆

須要先說明一個概念:淺拷貝和深拷貝指針

淺拷貝,兩個ArrayList在內存中的地址是不同的,可是他們中的元素都指向同一個地址,手繪一個圖解釋一下:code

深拷貝,不只ArrayList在內存中的地址是不同的,他們中的元素也不指向同一個地址,也手繪個圖解釋一下:cdn

深拷貝可能好理解一些,就是全部的東西都拷貝一下,徹底分離的狀態blog

ArrayList的**clone()**方法是淺拷貝,咱們來作個實驗:

public class Test {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add(String.valueOf(i));
        }
        //強制類型轉換
        ArrayList<String> list1 = (ArrayList<String>)list.clone();
        System.out.println("list" + list);
        System.out.println("list1" + list1);
        System.out.println("兩者對於某個特定元素是否相同 " + (list.get(1) == list1.get(1)));
        System.out.println("兩者的地址是否相同 " + (list == list1));
    }
}
複製代碼


9. 轉換爲數組

ArrayList是一個列表,和數組不同,不能直接用下標訪問元素,轉換爲數組就能夠了,轉換爲數組有兩種方法:

//按照列表中元素的順序,將列表中元素複製到一個數組
    public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }
複製代碼


這一種方法可能會拋出數組存儲異常和空指針異常,這裏講述兩種狀況(數組大小小於列表元素數量):

  1. 傳入的數組類型和列表元素相同或者是列表元素的基類型,直接將列表中元素賦值在數組內

  2. 傳入的數組類型不是列表元素的基類型,拋出數組存儲異常

public <T> T[] toArray(T[] a) {
        //傳入的數組大小更小
        if (a.length < size)
            //建立一個數組,類型是數組a的運行時類型
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
        System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }
複製代碼

傳入的數組 s 是Object類型的,符合上述的狀況一:

接下來是狀況二:

這樣子,ArrayList經常使用的方法就講完了,下一篇就會是與ArrayList有關的迭代器的使用了

相關文章
相關標籤/搜索