ArrayList <double> to double [],有3亿条目</double>

时间:2013-11-10 10:30:32

标签: java memory median

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

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

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

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

4 个答案:

答案 0 :(得分:6)

你的问题甚至比你意识到的还要糟糕,因为ArrayList<Double>的效率低于每个条目的8个字节。每个条目实际上都是一个对象,ArrayList保留了一组引用。 Double对象可能大约12个字节(某种类型标识符为4个字节,double本身为8个字节),对它的引用又增加了4个字节,总计最多16个字节每个条目,甚至不包括内存管理的开销等。

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

但仍有一些选择:

  • 循环输入两次;一次计算条目,一次将其读入正确大小的double[]。当然,这取决于你输入的性质是否可行。

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

  • 使用float代替double将内存需求减少一半,但代价是精确度。

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

答案 1 :(得分:2)

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

答案 2 :(得分:1)

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

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

由于你无论如何从数据库中获取数据,你可以告诉数据库给你中位数,而不是用Java来实现,这样可以节省用于传输数据的所有时间(和内存)。 / p>

答案 3 :(得分:1)

我同意,使用Trove4j TDoubleArrayList类(请参阅javadoc)为float存储double或TFloatArrayList。通过结合以前的答案,我们得到:

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