【问题标题】:ArrayList<Double> to double[] with 300 million entriesArrayList<Double> 到具有 3 亿个条目的 double[]
【发布时间】:2013-11-22 05:02:18
【问题描述】:

我正在使用 java 程序从数据库中获取一些数据。然后我计算一些数字并开始将它们存储在一个数组中。我正在使用的机器有 4 GB 的 RAM。现在,我不知道会有多少个数字,所以我使用ArrayList&lt;Double&gt;. 但我知道大概会有300 million numbers.

因此,由于一个 double 是 8 字节,因此该数组将消耗的内存粗略估计为 2.4 gig(可能更多,因为 ArrayList 的开销)。在此之后,我想计算这个数组的中位数,并使用org.apache.commons.math3.stat.descriptive.rank.Median 库,它将double[] 数组作为输入。 所以,我需要将ArrayList&lt;Double&gt; 转换为double[]

我确实看到了很多提出这个问题的问题,他们都提到没有办法绕过整个数组。现在这很好,但由于它们还在内存中维护这两个对象,这使我的内存需求达到了 4.8 gigs。现在我们遇到了一个问题,因为我们可用的总 RAM 有 4 个演出。

首先,我是否怀疑该程序会在某些时候给我一个正确的内存错误(它当前正在运行)?如果是这样,我如何计算中位数而不必分配双倍的内存?我想避免对数组进行排序,因为计算中位数是 O(n)。

【问题讨论】:

  • 写一个代码来显示你在做什么。
  • 如果数据在数据库中,为什么不使用数据库进行计算?您说的是从查询中检索 3 亿行吗?你用的是什么数据库?

标签: java memory median


【解决方案1】:

您的问题比您意识到的还要糟糕,因为ArrayList&lt;Double&gt; 的效率远低于每个条目 8 个字节。每个条目实际上是一个对象,ArrayList 保留一个引用数组。 Double 对象大概有 12 个字节(4 个字节用于某种类型标识符,8 个字节用于 double 本身),对它的引用又增加了 4 个字节,使每个条目的总数达到 16 个字节,甚至不包括内存管理等开销。

如果约束稍微宽一点,您可以实现自己的DoubleArray,它由double[] 支持,但知道如何调整自身大小。但是,调整大小意味着您必须同时在内存中保留旧数组和新数组的副本,这也超出了内存限制。

这仍然留下了一些选择:

  • 循环输入两次;一次计数条目,一次将它们读入正确大小的double[]。当然,这取决于您输入的性质。

  • 对最大输入大小(可能是用户可配置的)做出一些假设,并预先分配一个固定大小的double[]。仅使用已填充的部分。

  • 使用float 而不是double 将内存需求减少一半,但会牺牲一些精度。

  • 重新考虑您的算法以避免一次将所有内容都保存在内存中。

【讨论】:

  • 感谢您的回答.. 那么,double[] 数组是否需要不超过 8*size_of_array 的内存,或者是否还涉及一些开销?
  • double[] 没有任何元素开销
【解决方案2】:

有许多为基元创建动态数组的开源库。其中之一: http://trove.starlight-systems.com/

【讨论】:

    【解决方案3】:

    中值是排序列表中间的值。所以你不必使用第二个数组,你可以这样做:

    Collections.sort(myArray);
    final double median = myArray.get(myArray.size() / 2);
    

    因为无论如何你都是从数据库中获取数据,你可以告诉数据库给你中间值,而不是用 Java 来做,这也将节省传输数据的所有时间(和内存)。

    【讨论】:

    • 是的,但是 Collections.sort() 可能会决定使用合并排序,这将需要双倍内存并引发错误(对吗?)或者它会知道在给定数组大小的情况下使用快速排序吗?我不能直接使用sql,因为我对需要多个循环的数据进行了一些处理。
    • Collections.sort() 也不会因为双装箱开销而让您走得太远。如果您决定使用 O(n log n),过度分配然后使用 Arrays.sort() 的 3 参数变体:docs.oracle.com/javase/6/docs/api/java/util/Arrays.html
    【解决方案4】:

    我同意,使用 Trove4j TDoubleArrayList 类(参见 javadoc)存储双精度或使用 TFloatArrayList 存储浮点数。通过结合之前的答案,我们得到:

    // guess initialcapacity to remove requirement for resizing
    TDoubleArrayList data = new TDoubleArrayList(initialcapacity);
    // fill data
    data.sort();
    double median = data.get(data.size()/2);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-10-10
      • 2014-03-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多