在巨大的ArrayList

时间:2017-11-04 14:56:26

标签: java arraylist

当我用巨大的计算任务调试程序的性能时,我发现大多数时候向大ArrayList添加元素是通过添加1个元素来实现的。谁能解释为什么会发生这样的事情?

import java.util.ArrayList;

public class MainArr {
    ArrayList<Integer> normalList = new ArrayList<Integer>();

    public static void main(String[] args) {
        MainArr m = new MainArr();
        m.addElements();
    }

    public void addElements() {
        long startTime = System.currentTimeMillis();
        for (int j = 0; j < 20000000; j++) {
            long addTime = System.currentTimeMillis();
            this.normalList.add(j);
            if (System.currentTimeMillis() - addTime > 50) {
                System.out.println("slow index-" + j + " - time:" + (System.currentTimeMillis() - addTime));
            }
        }
        System.out.println("End after:" + (System.currentTimeMillis() - startTime));
    }
}

输出(总是相同的索引和时间):

slow index-4102267 - time:1184
slow index-6758091 - time:1444
slow index-12459620 - time:3124
slow index-14738741 - time:166
End after:6651

4 个答案:

答案 0 :(得分:2)

From the documentation

  

每个ArrayList实例都有一个容量。容量是用于存储列表中元素的数组的大小。它始终至少与列表大小一样大。当元素添加到ArrayList时,其容量会自动增加。除了添加元素具有恒定的摊销时间成本这一事实之外,未指定增长政策的细节。

因此,在引擎盖下,ArrayList是一个固定大小的数组,当它变满时会被复制并替换。因此,slow index标记ArrayList处发生的事情是ArrayList必须重新分配新的内部数组并将旧数组复制到新数组中。

如果你想加速,并且你大致知道ArrayList<Integer> normalList = new ArrayList<>(20000000); 的大小(如你的例子中所示),请使用ArrayList constructor来指定初始数组大小。

ArrayList

编辑 - 新答案:

通过上述答案,我获得了与@MichalLis相同的表现,所以我做了一些研究,我找到了不同的答案。

如果您使用示例代码并将int[]替换为普通的End after:2263 数组,则程序会吐出:

int[]

然后我用Integer[]数组替换了slow index-4022087 - time:2012 slow index-8150728 - time:948 slow index-14442110 - time:4886 End after:10309 数组,得到了这个:

ArrayList

事实证明,由于int实际上无法使用Integer并且必须使用int,因此创建新对象会对性能产生影响。 Integer s远远快于int[] s,因为前者是主要类型,而后者是包装对象。

如果您希望ArrayList的性能优势与ArrayList的调整大小有关,则您始终可以专门为int实现自己的public class IntArrayList { int[] array = new int[10]; int size = 0; public int get(int index){ return array[index]; } public void add(int value){ if(size == array.length){ resizeArray(); } array[size] = value; size++; } private void resizeArray(){ int[] newArray = new int[array.length * 2]; for(int i=0; i<array.length; i++){ newArray[i] = array[i]; } array = newArray; } public void set(int index, int value){ array[index] = value; } public int size(){ return size; } public void remove(int index){ for(int i=index; i<size-2; i++){ array[i] = array[i+1]; } size--; } } 类。< / p>

IntArrayList

这不是一个非常强大的实现,但它是一个起点。

使用上述End after:2315 实施输出OP代码:

Site

答案 1 :(得分:0)

我认为这是因为ArrayList不时增长以容纳更多元素。例如:如果数组列表中的数组的初始大小为8,那么在添加第9个元素时,将创建增大大小的新数组,并将旧数组复制到该数组。当元素数量变得太大时,该步骤开始消耗时间。 为防止这种情况,您应该使用LinkedList&lt;&gt;而不是ArrayList&lt;&gt;。

List<Integer> myHugeList = new LinkedList<Integer>();

但如果您的代码需要ArrayList&lt;&gt;由于索引查找的要求,您应该通过提供列表的预期最终大小来初始化您的ArrayList。

List<Integer> myHugeList = new ArrayList<Integer>(expectedSizeInInt);

答案 2 :(得分:0)

  

我发现大部分时间都要添加元素   通过添加1个元素来获取ArrayList。

我应该怀疑你的想法。您使用20,000,000(2000百万)个数字将它们添加到ArrayList中,并尝试在代码中间记录时间。 让我们检查您的代码,看看会发生什么:

for (int j = 0; j < 20000000; j++) {
            long addTime = System.currentTimeMillis();

此处的addTime等于所有这些步骤所消耗的时间:

j比较j < 20000000然后j++,最后计算System.currentTimeMillis()

您尚未逐一获取每项操作的时间,而是总和

然后我们有:

this.normalList.add(j);
            if (System.currentTimeMillis() - addTime > 50) {
                System.out.println("slow index-" + j + " - time:" + (System.currentTimeMillis() - addTime));
            }

所以,再次将j添加到数组中,并考虑时间如果它比那些 50

再次获得的是插入数组,比较和减法的总和。

由于您没有在代码中独立获得任何给定操作的时间,因此结论可能不正确。

重要的:

因为你已经在你的代码中创建了一个if (System.currentTimeMillis() - addTime > 50),这意味着,对于某些插入操作,你可能不满足条件。因此,本研究中遗漏的一个因素是您成功完成此病症的次数是多少次?

此外,您如何知道操作系统是否在您的流程中没有中断?

此外,您如何知道操作系统是否还没有阻止您的java进程将其资源提供给另一个进程?

答案 3 :(得分:0)

首先,你必须看看你做了哪些更多的任务。如果您主要修改列表,那么您应该使用 LinkedList ,而不是像 ArrayList 那样进行大小调整。如果您正在执行随机访问操作indexOf(..),get(index)等,那么您应该使用ArrayList。

您还可以通过给出初始大小来调整 ArrayList。

顺便说一下,似乎有一个名为 Brownies Collections 第三方库,它在一个名为 GapList 。

  

http://www.magicwerk.org/page-collections-overview.html

     

GapList结合了ArrayList和LinkedList的优势。它是   实现通过索引提供对元素的有效随机访问   (如ArrayList所做的那样)并同时有效地添加和删除   元素来回开始和结束(如LinkedList所做的那样)。它也是   利用应用程序中常见的引用位置   进一步提高性能,例如迭代列表......