【问题标题】:Smallest way to store a 2D array in java在java中存储二维数组的最小方法
【发布时间】:2015-07-15 20:07:44
【问题描述】:

我正在尝试生成一组应该(至少)为 6x6 的 2D int 数组。每个数组存储 0-6 的值。我尝试使用简单的HashSet<int[][]> 来存储它们(使用 512MB 内存),但很快我就得到了错误

Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded

只是进入程序的一小段路。

我想到的存储数组的选项:

  1. long 的形式将它们存储为基数 7。这仅适用于最多 24 (24.3717) 位,因为 long 不能超过 2^63 位。

  2. 将它们存储为 String(例如,{{0, 0, 0, 1}, {3, 6, 2, 0}} 将变为 "00013620")。这只会占用 4 倍的空间(我认为),因为 char 仍然是 1 个字节。

  3. 使用BitSetBigInteger 之类的东西?我不知道每个是什么或它们是如何工作的。

所以我的问题是:存储从 0 到 6 的 6 x 6 数组的最小方法是什么? 上述选项是否有效?更简单的方法?


注意:如果有必要,我可以使用 8GB 内存。

我的代码(如果你必须知道的话,它与国际象棋有关): n 是数组的大小(宽度和高度),应该可以达到(或超过)6。

public static HashSet<int[][]> getBoards(int[][] data, int zero, int num) {
    HashSet<int[][]> ret = new HashSet<int[][]>(0);

    if (zero == num) {
        ret.add(data);
    } else if (zero == 0) {
        for (int y = 0; y < n; y++) {
            for (int x = 0; x < n; x++) {
                for (int i = 1; i < 7; i++) {
                    int[][] d0 = new int[n][n];
                    d0[y][x] = i;
                    ret.addAll(getBoards(d0, 1, num));
                }
            }
        }
    } else {
        for (int y = 0; y < n; y++) {
            for (int x = 0; x < n; x++) {
                if (data[y][x] == 0) continue;

                HashSet<int[]> moves = getMoves(data[y][x], x, y);

                while (moves.iterator().hasNext()) {
                    int[] m = moves.iterator().next();

                    for (int i = 0; i < 6; i++) {
                        int[][] d0 = arrayCopy(data);
                        d0[m[0]][m[1]] = i;

                        ret.addAll(getBoards(d0, zero + 1, num));
                    }
                }
            }
        }
    }

    return ret;
}

public static HashSet<int[]> getMoves(int piece, int xPos, int yPos) {
    HashSet<int[]> ret = new HashSet<int[]>(0);

    for (int y = 0; y < n; y++) {
        for (int x = 0; x < n; x++) {
            if (x == xPos && y == yPos) continue;

            switch (piece) {
            case 1:
                if (y - yPos == 1 && Math.abs(x - xPos) == 1) ret.add(new int[] {y, x});
                break;
            case 2:
                if (Math.abs(y - yPos) + Math.abs(x - xPos) == 3 && x != xPos && y != yPos) ret.add(new int[] {y, x});
                break;
            case 3:
                if (Math.abs(y - yPos) == Math.abs(x - xPos)) ret.add(new int[] {y, x});
                break;
            case 4:
                if (y == yPos || x == xPos) ret.add(new int[] {y, x});
                break;
            case 5:
                if (Math.abs(y - yPos) == Math.abs(x - xPos) || y == yPos || x == xPos) ret.add(new int[] {y, x});
                break;
            case 6:
                if (Math.abs(y - yPos) <= 1 && Math.abs(x - xPos) <= 1) ret.add(new int[] {y, x});
                break;
            default:
                throw new IllegalArgumentException("Unknown Piece Number (" + piece + ")");
            }
        }
    }

    return ret;
}

完整的错误:

Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at ChessGenerator.arrayCopy(ChessGenerator.java:120)
at ChessGenerator.getBoards(ChessGenerator.java:71)
at ChessGenerator.getBoards(ChessGenerator.java:74)
at ChessGenerator.getBoards(ChessGenerator.java:74)
at ChessGenerator.getBoards(ChessGenerator.java:74)
at ChessGenerator.getBoards(ChessGenerator.java:74)
at ChessGenerator.getBoards(ChessGenerator.java:74)
at ChessGenerator.getBoards(ChessGenerator.java:74)
at ChessGenerator.getBoards(ChessGenerator.java:56)
at ChessGenerator.main(ChessGenerator.java:23)

编辑:正如@Louis 指出的那样,我使用HashSets 导致了上述错误,但是,我仍然内存不足

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at ChessGenerator.arrayCopy(ChessGenerator.java:119)
at ChessGenerator.getBoards(ChessGenerator.java:70)
at ChessGenerator.getBoards(ChessGenerator.java:73)
at ChessGenerator.getBoards(ChessGenerator.java:73)
at ChessGenerator.getBoards(ChessGenerator.java:73)
at ChessGenerator.getBoards(ChessGenerator.java:73)
at ChessGenerator.getBoards(ChessGenerator.java:73)
at ChessGenerator.getBoards(ChessGenerator.java:73)
at ChessGenerator.getBoards(ChessGenerator.java:58)
at ChessGenerator.main(ChessGenerator.java:23)

【问题讨论】:

  • @David 不一定
  • 我什至无法想象为什么存储 6x6 的 int 值数组会导致内存不足。你能显示一些代码吗?
  • HashSet&lt;int[][]&gt; 我认为您不希望数组的equals()hashCode() 使用HashSet。 java 中的数组正在实现hashCode()equals() 以匹配对象标识。具体来说,两个对象 [1,2,3] 和 [1,2,3] 将不相等,并且(可能)具有不同的 hashCode
  • 你应该阅读this question。实际上,您似乎并没有完全耗尽内存,因此使对象变小可能无济于事。
  • @David 为什么会这样,我该如何解决?

标签: java arrays memory memory-management out-of-memory


【解决方案1】:

如果您希望 HashSet 只保留唯一的 int[][]s,并消除重复,那将无法正常工作 - int[][](和所有数组)的 equalshashCode 实现是基于身份的。如果您一直依靠唯一性来保持不同数组的数量很小,那是行不通的;您将不得不将它们包装在实现正确 hashCodeequals 的类型中。

【讨论】:

  • 但是HashSet或类似的类是最小的方式吗?
  • 首先你必须确保你的代码确实在做你想做的事,然后你就可以担心了。现在,我几乎可以肯定它没有按照你的意思做。 HashSet 会为每个元素带来相当多的开销,但如果您需要唯一性,则需要唯一性,如果您想保持相对于 int[][] 内容的唯一性,您需要包装类型。
  • 现在,我更担心生成数组列表而不是让它唯一。
  • 在这种情况下,ArrayList 会消耗更少的内存。此外,如果它有帮助,如果您事先知道行数,一维数组可能会比二维数组占用更少的内存。
  • 谢谢,我用了ArrayList,程序运行完美。你能解释一下你的第一条评论吗?我希望它们是独一无二的。
【解决方案2】:

您似乎创建了很多板,很难遵循,但似乎您基本上生成了所有大小为 6X6 的数组的很大一部分,其中每个单元格可以具有任何值 1,2,.., 6.

此类数组的数量为 6^36 ~= 10^28。

这意味着,即使每个数组只有一个字节(不可能),您仍然需要 10^16 TB 来保存所有数组。

我建议您寻找不包括显式生成所有可能数组的替代方案。


附带说明,表示对象的最低位数是ceil(log_2(6^36)) = 94,但要获得最佳结果需要大量工作,我不建议这样做。

【讨论】:

  • 它不会(不应该)创建 所有 板的列表。即使是这样,它是否适用于 5x5? 4x4?它绝对适用于 3x3。
  • @ricky3350 要点是 - 你生成的数组太多了,你需要想办法在不显式生成所有数组的情况下实现你想要的。
【解决方案3】:

最直接但仍然节省内存的方法是将每个数组存储为两个longs,每个字段占用 3 位(总共 3*36=108 个有用位,开销为 20 个未使用位)。尽管理论上的限制小于这个值,但您几乎肯定希望您的结构与单词边界对齐,因此您并没有真正失去任何东西。但是,您赢得的是访问各个字段既简单又快速,只需要位掩码和移位操作。

我还会看看堆外存储选项,以消除所有对象开销。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-01-04
    • 2016-04-26
    • 2020-09-10
    • 1970-01-01
    • 2017-07-23
    • 1970-01-01
    • 2013-12-03
    • 1970-01-01
    相关资源
    最近更新 更多