考虑以下 Java 代码(完整、编译和运行良好)。
该代码创建一个包含 5,000,000 个整数(1 到 500 万)的数组,对其进行循环,并创建一个它找到的完美正方形的 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-exceeds-copy-over 周期,仍然比创建大小为 5,000,000 的 ArrayList 更快?