Java Arraylist大小声明和性能

时间:2013-10-28 12:21:13

标签: java arrays performance arraylist

考虑以下Java代码(完成,编译并运行正常)。

代码创建一个包含5,000,000个整数(1到5百万)的数组,在其上循环,并创建一个找到的正方形的ArrayList。使用天真的技术检测完美的正方形,而不是位操作,但这不是手头问题的焦点。

数学上,在1到5M之间,有2236个完美的正方形。因此,放置完美正方形的ArrayList的最终大小为2236。

import java.util.ArrayList;

public class PerfSquares {

    public static ArrayList<Integer> perfectSquares(int[] arr) {
        ArrayList<Integer> al = new ArrayList<Integer>();
    //  ArrayList<Integer> al = new ArrayList<Integer>(arr.length);

        for (int i = 0; i < arr.length; i++) {
            double root = Math.sqrt(arr[i]);
            int irt = (int) Math.floor(root);
            if (irt * irt == arr[i]) {
                al.add(arr[i]);
            }
        }
        return al;
    }

    public static void main(String[] args) {
        int[] arr = new int[5000000];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i + 1;
        }

        long s = System.currentTimeMillis();
        perfectSquares(arr);
        long e = System.currentTimeMillis();
        System.out.println(e - s);
    }
}

我想专注于ArrayList的声明。这两行,其中一行被注释掉:

  ArrayList<Integer> al = new ArrayList<Integer>();
//ArrayList<Integer> al = new ArrayList<Integer>(arr.length);

当我使用第一个声明(没有显式提供的大小)运行时,我看到的timediff是:

~96 milliseconds.

当我使用第二个声明( 显式提供的大小)时,timediff增加到:

~105 milliseconds

问题:

为什么会出现这种情况?第二种情况(提供的尺寸)不应该更快吗?

根据我的理解,在第一种情况下,当我们省略了ArrayList创建的size参数时,在幕后将初始化一个长度为10的数组。当超过此容量时,将分配具有更大容量(不确定更大)的新阵列,并且将复制先前的元素。

对于2236个元素并且没有指定初始大小,这个“超出限额 - 分配新 - 复制超过 - 追加更多直到上限”周期应该重复多次,减慢它。

因此,我期望提供的大小声明更快 - 因为分配将发生一次,并且没有容量超出/新阵列创建和复制过度发生。

或者这基本上是因为2236附加到ArrayList,即使有所有的cap-over-copy-over周期,仍然比创建大小为5,000,000的ArrayList更快?

4 个答案:

答案 0 :(得分:6)

你正在创造一个500万的arraylist,显然它更慢。你只需要2236.那是很多浪费。

例如,如果您将数组列表的大小更改为10k,则会看到时差缩小。

为了简化,只需多次尝试此测试,不同的顺序 -

public static void main(String[] args) {

   long timea = System.currentTimeMillis();

   // ArrayList<Integer> al = new ArrayList<Integer>();
   ArrayList<Integer> al = new ArrayList<Integer>(5000000);


    System.out.println(System.currentTimeMillis() - timea);

}

你会看到将一个arraylist初始化为500万(大约10毫秒)(在我的macbook上),而没有默认大小的那个几乎是瞬间完成的。这与您测试的订单无关。

答案 1 :(得分:2)

首先,您的测量方法存在缺陷。然而,在这些情况下,测量并不容易,因为对于如此大的阵列分配,每个新的后续可能会更慢。

至于你的问题:内存分配(甚至重新分配)是一项昂贵的操作。不是在使用new时 - 现在vms已针对许多小型短期对象进行了优化 - 但大多数时候JVM必须在较低的系统级别保留/分配(aka malloc())内存。此外,内存分配时间还取决于分配的内存量 - 您需要的越多,所需的时间就越长。

在您的情况下:完美正方形的数量是AFAIR易于计算。只需使用(Math.sqrt(arr.length) + 1)确定初始ArrayList尺寸,并立即获得完全正确的尺寸。

答案 2 :(得分:1)

因为内存分配通常是一个缓慢的操作。我计算了两种情况下的分配数量和新元素。

在这种情况下

ArrayList<Integer> al = new ArrayList<Integer>();

总共只有8317个元素的分配。在这种情况下

ArrayList<Integer> al = new ArrayList<Integer>(arr.length);

你有5000000个元素的单一分配。

答案 3 :(得分:0)

当您致add()已满时ArrayList,它会自动增长50%。因此,它将足够快,并且不会有如此多的内存分配。