【问题标题】:Java Arraylist size declaration and performanceJava Arraylist 大小声明和性能
【发布时间】:2013-11-07 05:15:37
【问题描述】:

考虑以下 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 更快?

【问题讨论】:

  • 只运行一次,System.currentTimeMillis() 的精度如此差,并获得如此小的差异,导致毫无意义的结果。执行 1000 次,然后使用 System.nanoTime() 开始测量接下来的 1000 次执行,然后平均结果。
  • 不要创建具有完整数组大小的ArrayList,而是尝试使用(int) Math.sqrt(arr.size()),这大约是完美正方形的数量。如果您想确定,请添加一些。
  • @SidCool 坏主意。当我们谈论性能时,LinkedList从来没有一个选项。
  • @Aaron:使用LinkedList,即使追加和迭代也会慢得多(2 倍加上 GC 开销)。你只有一个好的操作:在Iterator的位置移除。这几乎没有。虽然 LinkedList 有很好的用例,但它们比大多数人预期的要少得多。
  • @Aaron 我什至认为将 LinkedList 添加到标准 Java API 是一个错误。 LinkedList 仅适用于非常罕见和特定的情况。在我 6 年的 Java 经验中我从未使用过它。与 TreeMap/Set、Deque 等数据结构不同。 On Github LinkedList 的使用频率高于 TreeMap、TreeSet、Deque 和 ArrayDeque放在一起。这令人失望。

标签: java arrays performance arraylist


【解决方案1】:

您正在创建一个包含 500 万个数组的数组列表,因此显然速度较慢。你只需要 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);

}

您会看到将数组列表初始化为 500 万需要大约 10 毫秒(在我的 macbook 上),而没有默认大小的数组几乎是瞬时的。无论您测试哪种顺序,这都是一样的。

【讨论】:

  • 它一点也不慢,如果你正确地进行基准测试,你最终会得到低于 2% 的差异。
  • @ThomasJungblut 它更慢 - 运行测试看看。
  • 运行 caliper 基准测试并亲自查看结果:gist.github.com/thomasjungblut/7196135 结果:99.6 ms vs. 101 ms。复制这么小的数字与直接分配更大的数组一样昂贵。
  • 所有这些迭代是否都在同一个 VM 中运行?如果是这样,那是一个不同的测试 - 一次运行上述代码很明显,无论您测试哪个顺序(首先是初始化列表,或者只是先简单列表),大小为 5000000 的一个明显更长的时间至少 9 倍十。 OP 发现它不直观 - 原因是大量分配。
【解决方案2】:

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

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

在您的情况下:完美正方形的数量很容易计算。只需使用(Math.sqrt(arr.length) + 1) 确定初始ArrayList 大小并立即得到完全正确的大小。

【讨论】:

    【解决方案3】:

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

    在这种情况下

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

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

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

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

    【讨论】:

    • 看起来它的相对大小是昂贵的分配,而不是分配本身。
    【解决方案4】:

    当您在已满的 ArrayList 上调用 add() 时,它会自动增长 50%。所以它很快就会足够大,不会有那么多内存分配。

    【讨论】:

      猜你喜欢
      • 2013-02-15
      • 1970-01-01
      • 2016-12-16
      • 2011-01-04
      • 2020-12-30
      • 1970-01-01
      • 1970-01-01
      • 2012-08-13
      • 2021-02-21
      相关资源
      最近更新 更多