我正在使用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)。
答案 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);