簡體   English   中英

為什么要以初始容量啟動 ArrayList?

[英]Why start an ArrayList with an initial capacity?

ArrayList的常用構造函數是:

ArrayList<?> list = new ArrayList<>();

但是還有一個重載的構造函數,它的初始容量帶有一個參數:

ArrayList<?> list = new ArrayList<>(20);

當我們可以隨心所欲地附加到它時,為什么創建一個具有初始容量的ArrayList很有用?

如果您事先知道ArrayList<\/code>的大小,那么指定初始容量會更有效。 如果你不這樣做,隨着列表的增長,內部數組將不得不重復重新分配。

最終列表越大,通過避免重新分配節省的時間就越多。

也就是說,即使沒有預先分配,在ArrayList<\/code>的后面插入n<\/code>元素也可以保證總共花費O(n)<\/code>時間。 換句話說,附加一個元素是一個攤銷的常數時間操作。 這是通過讓每個重新分配以指數方式增加數組的大小來實現的,通常是1.5<\/code> 。 使用這種方法,操作總數可以顯示為O(n)<\/code><\/a> 。

因為ArrayList是一個動態調整大小的數組數據結構,這意味着它被實現為一個具有初始(默認)固定大小的數組。 當它被填滿時,數組將被擴展為一個雙倍大小的數組。 此操作成本高昂,因此您希望盡可能少。

因此,如果您知道上限是 20 個項目,那么創建初始長度為 20 的數組比使用默認值(例如 15)更好,然后將其調整為15*2 = 30並且僅使用 20 而浪費周期為擴展。

PS - 正如 AmitG 所說,擴展因子是特定於實現的(在這種情況下是(oldCapacity * 3)/2 + 1

Arraylist 的默認大小為10<\/strong> 。

/**
 * Constructs an empty list with an initial capacity of ten.
 */
public ArrayList() {
    this(10);
}   

實際上,我在 2 個月前就該主題寫了一篇博文。 這篇文章是針對 C# 的List<T>但 Java 的ArrayList有一個非常相似的實現。 由於ArrayList是使用動態數組實現的,因此它會根據需要增加大小。 所以容量構造函數的原因是為了優化目的。

當這些調整大小操作之一發生時,ArrayList 將數組的內容復制到一個新數組中,該數組的容量是舊數組的兩倍。 此操作在O(n)時間內運行。

例子

以下是ArrayList如何增加大小的示例:

10
16
25
38
58
... 17 resizes ...
198578
297868
446803
670205
1005308

所以列表從容量10開始,當添加第 11 個項目時,它增加了50% + 116 在第 17 項, ArrayList再次增加到25 ,依此類推。 現在考慮我們正在創建一個列表的示例,其中所需容量已被稱為1000000 在沒有大小構造函數的情況下創建ArrayList將調用ArrayList.add 1000000次,這通常需要O(1)O(n)調整大小。

1000000 + 16 + 25 + ... + 670205 + 1005308 = 4015851 次操作

使用構造函數進行比較,然后調用保證在O(1)中運行的ArrayList.add

1000000 + 1000000 = 2000000 次操作

Java 與 C#

Java 同上,從10開始,每次調整大小增加50% + 1 C# 從4開始,並且增加得更加積極,每次調整大小都會翻倍。 上面的1000000為 C# 添加示例使用3097084操作。

參考

將 ArrayList 的初始大小設置為ArrayList<>(100)<\/code>可以減少重新分配內部存儲器的次數。

例子:<\/strong>

ArrayList example = new ArrayList<Integer>(3);
example.add(1); // size() == 1
example.add(2); // size() == 2, 
example.add(2); // size() == 3, example has been 'filled'
example.add(3); // size() == 4, example has been 'expanded' so that the fourth element can be added. 

ArrayList 可以包含許多值,並且在進行大量初始插入時,您可以告訴 ArrayList 在開始時分配更大的存儲空間,以免在嘗試為下一項分配更多空間時浪費 CPU 周期。 因此,在開始時分配一些空間更有效。

"

這是為了避免為每個對象重新分配可能的努力。

int newCapacity = (oldCapacity * 3)/2 + 1;

在內部創建了new Object[]
當您在數組列表中添加元素時,JVM 需要努力創建new Object[] 如果您沒有上述代碼(您認為的任何算法)進行重新分配,那么每次調用arraylist.add()時都必須創建new Object[] ,這是沒有意義的,我們正在浪費時間將大小增加 1每個要添加的對象。 所以最好用下面的公式來增加Object[]的大小。
(JSL 使用下面給出的預測公式來動態增長 arraylist,而不是每次增長 1。因為增長需要 JVM 的努力)

int newCapacity = (oldCapacity * 3)/2 + 1;

我認為每個 ArrayList 的初始容量值為“10”。 所以無論如何,如果您創建一個 ArrayList 而不在構造函數中設置容量,它將使用默認值創建。

"

我會說它是一種優化。 沒有初始容量的 ArrayList 將有大約 10 個空行,並且會在您進行添加時擴展。

要獲得一個包含您需要調用trimToSize()<\/a>的項目數量的列表

根據我對ArrayList的經驗,提供初始容量是避免重新分配成本的好方法。 但它有一個警告。 上面提到的所有建議都表明,只有在知道元素數量的粗略估計時才應該提供初始容量。 但是當我們試圖在不知道任何想法的情況下給出初始容量時,保留和未使用的內存量將是一種浪費,因為一旦列表填充到所需數量的元素,它可能永遠不需要。 我的意思是,我們可以在分配容量時一開始就務實,然后找到一種聰明的方法來了解運行時所需的最小容量。 ArrayList 提供了一個名為ensureCapacity(int minCapacity)的方法。 但后來,人們找到了一個聰明的方法......

我已經測試了有和沒有 initialCapacity 的 ArrayList,我得到了令人驚訝的結果
當我將 LOOP_NUMBER 設置為 100,000 或更少時,結果是設置 initialCapacity 是有效的。

list1Sttop-list1Start = 14
list2Sttop-list2Start = 10


但是當我將 LOOP_NUMBER 設置為 1,000,000 時,結果變為:

list1Stop-list1Start = 40
list2Stop-list2Start = 66


最后,我無法弄清楚它是如何工作的?!
示例代碼:

 public static final int LOOP_NUMBER = 100000;

public static void main(String[] args) {

    long list1Start = System.currentTimeMillis();
    List<Integer> list1 = new ArrayList();
    for (int i = 0; i < LOOP_NUMBER; i++) {
        list1.add(i);
    }
    long list1Stop = System.currentTimeMillis();
    System.out.println("list1Stop-list1Start = " + String.valueOf(list1Stop - list1Start));

    long list2Start = System.currentTimeMillis();
    List<Integer> list2 = new ArrayList(LOOP_NUMBER);
    for (int i = 0; i < LOOP_NUMBER; i++) {
        list2.add(i);
    }
    long list2Stop = System.currentTimeMillis();
    System.out.println("list2Stop-list2Start = " + String.valueOf(list2Stop - list2Start));
}

我在 windows8.1 和 jdk1.7.0_80 上測試過

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM