java - 在巨大的ArrayList中添加元素会暂时停止

标签 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

最佳答案

From the documentation :

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 是一个固定大小的数组,当它变满时,它会被复制和替换。因此,慢速索引标记处发生的情况是 ArrayList 必须重新分配一个新的内部数组并将旧数组复制到新数组中。

如果您想要加速,并且您大致知道 ArrayList 将有多大(如您的示例中所示),请使用 ArrayList constructor它允许您指定初始数组大小。

ArrayList<Integer> normalList = new ArrayList<>(20000000);

编辑 - 新答案:

通过上述答案,我得到了与@MichalLis相同的性能,所以我做了一些研究,然后发现了不同的答案。

如果您采用示例代码并将 ArrayList 替换为普通的旧 int[] 数组,则程序会输出:

End after:2263

然后我用 Integer[] 数组替换了 int[] 数组,得到了这个:

slow index-4022087 - time:2012
slow index-8150728 - time:948
slow index-14442110 - time:4886
End after:10309

事实证明,由于 ArrayList 实际上不能使用 int 而必须使用 Integer,因此创建新的数组会对性能产生影响对象。 intInteger 快得多,因为前者是基本类型,而后者是包装对象。

如果您希望获得 int[] 的性能优势以及 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--;
    }
}

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

使用上述IntArrayList实现的OP代码的输出:

End after:2315

关于java - 在巨大的ArrayList中添加元素会暂时停止,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47112021/

相关文章:

java - 以多对一关系归档的版本导致对象引用未保存的 transient 实例

java - OkHttp -queuedCallsCount() 和 runningCallsCount() 有什么区别?

java - 我可以尝试放置两个 Select 吗?

java - 最终静态方法的行为

java - 将值插入 HashMap 无法正常工作

java - 将字符串与数组列表进行比较,看看该字符串是否包含列表中的任何内容

java - 从选项卡 Pane 中检索文本区域

c# - 将点的 ArrayList 转换为字节数组以存储在 SQL 数据库中

java - fragment 中传递的 Arraylist 为空

Java - ArrayList 未检测到重复值