數(shù)組

基本概念

數(shù)組是一種線性表數(shù)據(jù)結構。它用一組連續(xù)的內(nèi)存空間,來存儲一組具有相同類型的數(shù)據(jù)。
隨機訪問時間復雜度O(1),添加和刪除時間復雜度O(n)。
添加和刪除最好的情況是添加和刪除最后一位,時間復雜度O(1),最壞情況,移動整個數(shù)組,時間復雜度O(n)。

插入和刪除改進思想

插入

如果數(shù)組中的數(shù)據(jù)是有序的,我們在某個位置插入一個新的元素時,就必須按照剛才的方法搬移k之后的數(shù)據(jù)。
但是,如果數(shù)組中存儲的數(shù)據(jù)并沒有任何規(guī)律,數(shù)組只是被當作一個存儲數(shù)據(jù)的集合。
假設數(shù)組 a[10]中存儲了如下 5 個元素:a,b,c,d,e。
我們現(xiàn)在需要將元素 x 插入到第 3 個位置。我們只需要將 c 放入到 a[5],將 a[2]賦值為 x 即可。最后,數(shù)組中的元素如下: a,b,x,d,e,c。
這種插入方式會將O(n)降到O(1)

刪除

復雜度與插入完全一致
數(shù)組 a[10]中存儲了 8 個元素:a,b,c,d,e,f,g,h?,F(xiàn)在,我們要依次刪除 a,b,c 三個元素。
我們可以先記錄下已經(jīng)刪除的數(shù)據(jù)。每次的刪除操作并不是真正地搬移數(shù)據(jù),只是記錄數(shù)據(jù)已經(jīng)被刪除。當數(shù)組沒有更多空間存儲數(shù)據(jù)時,我們再觸發(fā)執(zhí)行一次真正的刪除操作,這樣就大大減少了刪除操作導致的數(shù)據(jù)搬移。

內(nèi)存尋址公式

對于一維長度為k的數(shù)組,a[k]的地址為:

a[k]_address = base_address + k * type_size

對于 m * n 的數(shù)組,a [ i ][ j ] (i < m,j < n)的地址為:

address = base_address + ( i * n + j) * type_size

ArrayList源碼

添加

添加元素到尾部

public boolean add(E e) {
    // 數(shù)組大小是否足夠,不夠就擴容
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    // 直接賦值
    elementData[size++] = e;
    return true;
}

擴容代碼

1. 初始化數(shù)組大小時,是否有給定初始值,以給定的大小為準,沒有給默認擴容到10.
2. 期望的最小容量大于當前數(shù)組的長度,那么就擴容。
private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    // 擴容后的數(shù)組容量是原先的1.5倍,擴大了1/2
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 如果擴容后的容量依然小于期望的最小容量,那么擴容后的值就等于期望值
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    // 如果擴容后的值 > jvm 所能分配的數(shù)組的最大值,那么就用 Integer 的最大值
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
   
   // 將原先數(shù)組的值拷貝到新數(shù)組
    elementData = Arrays.copyOf(elementData, newCapacity);
}

在指定位置添加元素

public void add(int index, E element) {
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

    ensureCapacityInternal(size + 1);  // Increments modCount!!
    /**
     * @param src     被拷貝的數(shù)組
     * @param srcPos  從數(shù)組那里開始
     * @param dest    目標數(shù)組
     * @param destPos 從目標數(shù)組那個索引位置開始拷貝
     * @param length  拷貝的長度 
     * 此方法是沒有返回值的,通過 dest 的引用進行傳值
     */
    System.arraycopy(elementData, index, elementData, index + 1,size - index);
    elementData[index] = element;
    size++;
}

刪除

給定要刪除的元素的index

public E remove(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

    modCount++;
    E oldValue = (E) elementData[index];

    int numMoved = size - index - 1;
    if (numMoved > 0)
        // index之后的全部數(shù)據(jù)往前移一位
        System.arraycopy(elementData, index+1, elementData, index,numMoved);
        // 最后一位置空,方便垃圾回收
    elementData[--size] = null; // clear to let GC do its work

    return oldValue;
}

給定要刪除的元素

public boolean remove(Object o) {
    if (o == null) {
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                //和上面的remove(int index)處理方式基本一致
                fastRemove(index);
                return true;
            }
    } else {
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                fastRemove(index);
                return true;
            }
    }
    return false;
}

迭代器

用的三個方法:
hasNext 還有沒有值可以迭代
next 如果有值可以迭代,迭代的值是多少
remove 刪除當前迭代的值

hasNext

public boolean hasNext() {
   //cursor 表示下一個元素的位置,size 表示實際大小,如果兩者相等,說明已經(jīng)沒有元素可以迭代了,如果不等,說明還可以迭代
  return cursor < limit;
}

next

public E next() {
    // 注意這個,由上面源碼可知,修改和刪除都會引起modCount變化
    // 如果在迭代器遍歷的時候?qū)线M行修改就會報錯
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
    // 本次迭代元素的索引值
    int i = cursor;
    if (i >= limit)
        throw new NoSuchElementException();
    Object[] elementData = ArrayList.this.elementData;
    if (i >= elementData.length)
        throw new ConcurrentModificationException();
    // 為下一次迭代做準備
    cursor = i + 1;
    return (E) elementData[lastRet = i];
}

remove

public void remove() {
    if (lastRet < 0)
        throw new IllegalStateException();
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();

    try {
        // 通過給定index刪除元素
        ArrayList.this.remove(lastRet);
        cursor = lastRet;
        // 防止重復刪除元素
        lastRet = -1;
          // 刪除元素時 modCount 的值已經(jīng)發(fā)生變化,在此賦值給 expectedModCount
        // 這樣下次迭代時,兩者的值是一致的了
        expectedModCount = modCount;
        limit--;
    } catch (IndexOutOfBoundsException ex) {
        throw new ConcurrentModificationException();
    }
}
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容