ArrayList擴容機制源碼及測試

Java SE APIs

1時間複雜度

2擴容機制

3非線程安全

詳情(結合部分源碼)

1時間複雜度

The sizeisEmptygetsetiterator, and listIterator operations run in constant time. The add operation runs in amortized constant time, that is, adding n elements requires O(n) time. All of the other operations run in linear time (roughly speaking). The constant factor is low compared to that for the LinkedList implementation.

Each ArrayList instance has a capacity. The capacity is the size of the array used to store the elements in the list. It is always at least as large as the list size. As elements are added to an ArrayList, its capacity grows automatically. The details of the growth policy are not specified beyond the fact that adding an element has constant amortized time cost.

最後一句話是指在ArrayList中的增長策略(即動態擴容機制)沒有具體的規定,除了一個重要的事實,即添加元素的攤銷時間成本是常數。在Java的ArrayList實現中,雖然具體的增長策略沒有公開規定,但是有一些常見的實踐:

  1. 增長因子: 通常,ArrayList會選擇一個適當的增長因子來決定何時擴容。增長因子表示內部數組的大小將如何增加。常見的增長因子是1.5,即當需要擴容時,新數組的大小將是當前數組大小的1.5倍。這是爲了減少頻繁的擴容操作,同時避免浪費大量的內存。
  2. 擴容操作: 當需要擴容時,ArrayList會創建一個新的內部數組,通常是當前數組大小的1.5倍。然後,它會將所有元素從舊數組複製到新數組,並更新內部引用,使其指向新數組。舊數組會被垃圾回收。這種方式使得添加元素的攤銷時間成本是常數。

雖然具體的增長策略沒有被硬性規定,但上述常見實踐通常是在ArrayList的不同Java版本中使用的。這種增長策略旨在平衡內存效率和性能。在實際使用ArrayList時,你通常無需擔心增長策略的細節,因爲Java的ArrayList會自動處理擴容操作,以確保它能夠有效地處理不同規模的數據集合。

2擴容機制

添加元素

private void add(E e, Object[] elementData, int s) {
    if (s == elementData.length)
        elementData = grow();
    elementData[s] = e;
    size = s + 1;
}

public boolean add(E e) {
    modCount++;
    add(e, elementData, size);
    return true;
}

擴容(總之涉及Arrays.copyOf

private Object[] grow(int minCapacity) {
    int oldCapacity = elementData.length;
    if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        int newCapacity = ArraysSupport.newLength(oldCapacity,
                minCapacity - oldCapacity, /* minimum growth */
                oldCapacity >> 1           /* preferred growth */);
        return elementData = Arrays.copyOf(elementData, newCapacity);
    } else {
        return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
    }
}

private Object[] grow() {
    return grow(size + 1);
}

3非線程安全

Note that this implementation is not synchronized. If multiple threads access an ArrayList instance concurrently, and at least one of the threads modifies the list structurally, it must be synchronized externally. (A structural modification is any operation that adds or deletes one or more elements, or explicitly resizes the backing array; merely setting the value of an element is not a structural modification.) This is typically accomplished by synchronizing on some object that naturally encapsulates the list. If no such object exists, the list should be "wrapped" using the Collections.synchronizedList method. This is best done at creation time, to prevent accidental unsynchronized access to the list:

   List list = Collections.synchronizedList(new ArrayList(...));

擴容機制實驗

import java.util.ArrayList;

public class ArrayListResizeTest {
    public static void main(String[] args) {
        int initialCapacity = 10; // 初始容量
        int numElementsToAdd = 1000000; // 要添加的元素數量

        // 創建一個ArrayList並設置初始容量
        ArrayList<Integer> arrayList = new ArrayList<>(initialCapacity);

        long startTime = System.currentTimeMillis();

        // 添加大量元素
        for (int i = 0; i < numElementsToAdd; i++) {
            arrayList.add(i);
        }

        long endTime = System.currentTimeMillis();
        long duration = endTime - startTime;

        System.out.println("元素數量: " + numElementsToAdd);
        System.out.println("初始容量: " + initialCapacity);
        System.out.println("實際容量: " + arrayList.size());
        System.out.println("添加元素耗時: " + duration + " 毫秒");
    }
}

結果1:

元素數量: 1000000
初始容量: 10
實際容量: 1000000
添加元素耗時: 86 毫秒

結果2:

元素數量: 1000000
初始容量: 1000000
實際容量: 1000000
添加元素耗時: 44 毫秒

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章