通常的构造函数ArrayList是:
ArrayList<?> list = new ArrayList<>();但是,还有一个带有初始容量参数的重载构造函数:
ArrayList<?> list = new ArrayList<>(20);ArrayList当我们可以随意添加初始容量时,为什么创建初始容量有用呢?
通常的构造函数ArrayList是:
ArrayList<?> list = new ArrayList<>();但是,还有一个带有初始容量参数的重载构造函数:
ArrayList<?> list = new ArrayList<>(20);ArrayList当我们可以随意添加初始容量时,为什么创建初始容量有用呢?
Answers:
如果您事先知道 ArrayList指定初始容量会更有效。如果不这样做,则随着列表的增加,内部数组将不得不反复重新分配。
最终列表越大,避免重新分配节省的时间越多。
也就是说,即使没有预先分配,也要保证n在元素的后面插入元素ArrayList会花费总O(n)时间。换句话说,附加元素是摊销的固定时间操作。这是通过使每个重新分配的数组大小通常成倍增加的方式来实现的1.5。使用这种方法,操作总数可以显示为O(n)。
O(n log n)将进行log n工作n。这是一个高估(尽管在技术上正确使用大O,因为它是上限)。总共复制s + s * 1.5 + s * 1.5 ^ 2 + ... + s * 1.5 ^ m(这样s * 1.5 ^ m <n <s * 1.5 ^(m + 1))个元素。我的求和技巧不好,所以我无法为您提供精确的数学信息(调整因子2的大小为2n,因此给定或取一个小常数可能为1.5n),但事实并非如此。 t斜眼看到该总和最多是一个大于n的常数。因此,它需要O(k * n)个副本,这当然是O(n)。
                    Arraylist的默认大小为10。
    /**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
    this(10);
    } 因此,如果要添加100条或更多条记录,则可以看到内存重新分配的开销。
ArrayList<?> list = new ArrayList<>();    
// same as  new ArrayList<>(10);      因此,如果您对将要存储在Arraylist中的元素数量有任何了解,最好以该大小创建Arraylist而不是从10开始,然后继续增加它。
private static final int DEFAULT_CAPACITY = 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% + 1为16。在第17项上,ArrayList再次增加到25,依此类推。现在来看一个示例,其中我们正在创建一个列表,其中所需的容量已经称为1000000。创建ArrayList不带size构造函数的会调用通常ArrayList.add 1000000需要O(1)或调整大小为O(n)的时间。
1000000 + 16 + 25 + ... + 670205 + 1005308 = 4015851运算
使用构造函数进行比较,然后调用ArrayList.add保证可以在O(1)中运行的函数。
1000000 + 1000000 = 2000000操作
Java如上,从开始并在处10增加每个调整大小50% + 1。C#始于C#,4并且更加积极地增加,每次调整大小都会加倍。1000000以上是C#使用的添加示例3097084操作。
将ArrayList的初始大小设置为例如ArrayList<>(100),可以减少必须重新分配内部存储器的次数。
例:
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的大小通常加倍(尽管请注意,新大小取决于您的实现)。以下是Oracle的引文:
“每个ArrayList实例都有一个容量。容量是用于在列表中存储元素的数组的大小。它总是至少与列表大小一样大。当将元素添加到ArrayList时,其容量会自动增长。除了添加元素具有固定的摊销时间成本外,没有指定增长策略的细节。”
显然,如果您不知道要保持哪种范围,则设置大小可能不是一个好主意-但是,如果您确实有特定的范围,则设置初始容量将提高内存效率。
ArrayList可以包含许多值,并且在进行较大的初始插入时,可以告诉ArrayList开始分配更大的存储,以免在尝试为下一项分配更多空间时浪费CPU周期。因此,在开始时分配一些空间会更有效。
这是为了避免为每个对象进行重新分配而可能做出的努力。
int newCapacity = (oldCapacity * 3)/2 + 1;内部new Object[]创建。当您在arraylist中添加元素时,
JVM需要努力创建   new Object[]。如果你没有上面的代码(任何算法中你认为)的重新分配,然后每次当你调用arraylist.add()然后new Object[]必须创建这是没有意义的,我们正在失去的时间由1对每一个要添加对象的规模日益扩大。因此最好Object[]用以下公式增加的大小。
(JSL使用下面给出的预测公式来动态增长数组列表,而不是每次都增长1。因为要增长,它需要JVM的努力)
int newCapacity = (oldCapacity * 3)/2 + 1;add-它已经在内部使用了一些增长公式。因此,问题没有得到回答。
                    int newCapacity = (oldCapacity * 3)/2 + 1;存在于ArrayList类中的。您是否仍未回答?
                    ArrayList摊销的重新分配。问题是:为什么要对初始容量完全使用非标准值?除此之外:“线间阅读”不是技术答案中所需要的。;-)
                    我会说这是一种优化。没有初始容量的ArrayList将有约10个空行,并且在执行添加操作时会扩展。
要获得具有确切数量的项目的列表,您需要调用trimToSize()
根据我的经验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上进行了测试