用于存储矩阵的Java大数据结构

时间:2009-11-10 22:21:24

标签: java performance memory matrix data-structures

我需要存储一个包含邮政编码的二维矩阵,以及每个邮编之间的距离。我的客户端有一个计算距离的应用程序,然后存储在Excel文件中。目前,有952个地方。所以矩阵将有952x952 = 906304个条目。

我试图将其映射到HashMap [Integer,Float]。 Integer是两个字符串的哈希码,例如: “A”和“B”。浮点值是它们之间以km为单位的距离。

在填写数据时,我会在205k条目后运行OutOfMemoryExceptions。你有一个提示,我怎么能以聪明的方式存储它?我甚至不知道在整个记忆中是否都很聪明。我的选择是SQL和MS Access ......

问题是我需要非常快速地访问数据,这可能是我选择HashMap的原因,因为它在O(1)中运行以进行查找。

Thansk的回复和建议!

9 个答案:

答案 0 :(得分:8)

2d数组会更有效。 您可以使用小型散列映射将952个位置映射到0到951之间的数字。 然后,就这样做:

float[][] distances= new float[952][952];

要查找,只需使用两个哈希查找将两个位置转换为两个整数,并将它们用作二维数组的索引。

通过这种方式,你可以避免浮动的装箱,以及大型hashmap的内存开销。

但是,906304真的不是那么多条目,你可能只需要增加Xmx最大堆大小

答案 1 :(得分:5)

我原本以为你可以动态计算距离。据推测有人已经这样做了,所以你只需要找出他们使用的算法和输入数据;例如每个邮政编码的名义中心的经度/纬度。

编辑:有两种常用的算法可以找到由经度/纬度对给出的两点之间的(近似)测地距离。

  • Vicenty formula基于椭圆近似。它更准确,但实施起来更复杂。

  • Haversine formula基于球面近似。它不太准确(0.3%),但实施起来更简单。

答案 2 :(得分:2)

您可以简单地增加JVM可用的内存吗?

java -Xmx512m ...

通过 default ,最大内存配置为64Mb。一些更多的调整技巧here。如果你能做到这一点,那么你可以保持数据在进行中并最大化性能(即你不需要动态计算)。

答案 3 :(得分:2)

我赞同Chi和Benjamin的答案,因为他们告诉你你需要做什么,但是当我在这里时,我想强调直接使用两个字符串的哈希码会让你陷入困境。您可能会遇到哈希冲突的问题。

如果你连接两个字符串(小心使用不能出现在地方指示符中的分隔符),并让HashMap发挥其魔力,这不会是一个问题,但方法你建议,使用两个字符串的哈希码作为密钥,这会让你陷入麻烦。

答案 4 :(得分:1)

您只需要更多内存。在启动Java进程时,请将其启动:

  

java -Xmx256M MyClass

-Xmx定义了最大堆大小,因此这表示进程最多可以为堆使用256 MB内存。如果你仍然用光,请继续将该数字提高,直至达到物理极限。

答案 5 :(得分:1)

最近我为我的硕士论文设定了类似的要求。

我最后使用的是使用double[]而不是double[][]的Matrix类,以减轻双重deref费用(data[i]是一个数组,然后是array[i][j]这是一个double),同时允许VM分配一大块连续的内存:

public class Matrix {

    private final double data[];
    private final int rows;
    private final int columns;

    public Matrix(int rows, int columns, double[][] initializer) {
        this.rows = rows;
        this.columns = columns;
        this.data = new double[rows * columns];

        int k = 0;

        for (int i = 0; i < initializer.length; i++) {
            System.arraycopy(initializer[i], 0, data, k, initializer[i].length);
            k += initializer[i].length;
        }
    }

    public Matrix set(int i, int j, double value) {
        data[j + i * columns] = value;
        return this;
    }

    public double get(int i, int j) {
        return data[j + i * columns];
    }
}

这个类应该使用比HashMap更少的内存,因为它使用原始数组(不需要装箱):它只需要906304 * 8 ~ 8 Mb(对于 double < / strong> s)或906304 * 4 ~ 4 Mb(对于浮动)。我的2美分。

NB 为简单起见,我省略了一些健全性检查

答案 6 :(得分:1)

以上有关堆大小的建议会有所帮助。但是,我不确定您是否准确描述了矩阵的大小。

假设您有4个位置。然后,您需要评估A-> B,A-> C,A-> D,B-> C,B-> D,C-> D之间的距离。这表明你的HashMap中有六个条目(4个选择2)。

这会让我相信你的HashMap的实际最佳大小是(952选择2)= 452,676; NOT 952x952 = 906,304。

当然,这只是假设您只存储单向关系(即从A-> B,但不是从B-> A,因为这是多余的),我建议你这样做,因为你是已经遇到内存空间问题。

编辑:应该说矩阵的大小不是最优的,而不是说描述不准确。

答案 7 :(得分:1)

Stephen C.有一个很好的观点:如果距离像苍蝇一样,那么你可以通过动态计算来节省内存。所有你需要的是952邮政编码的经度和纬度空间,然后你可以使用vicenty formula在需要时进行计算。这将使你的内存使用O(n)在zipcodes。

当然,该解决方案会做出一些假设,在您的特定情况下可能会变得虚假,即您拥有邮政编码的经度和纬度数据,并且您关注的是如同苍蝇的距离和不像行车路线那样复杂。

如果这些假设是正确的,那么如果您需要处理更大的数据集,那么为一大堆内存交换一些计算可能会帮助您在将来扩展。

答案 8 :(得分:0)

创建一个新的类,其中包含2个位置名称的插槽。它始终将字母顺序的名字放在第一个插槽中。给它一个适当的equals和hashcode方法。给它一个compareTo(例如,按名称的字母顺序排序)。把它们全部扔进阵列中。解决。

此外,hash1 = hash2并不意味着object1 = object2。不要这样做。这是一个黑客。