我正在尝试生成一组应该(至少)为6x6的2D int
数组。每个数组都存储0-6的值。我尝试使用简单的HashSet<int[][]>
来存储它们(512MB的内存),我很快就收到了错误
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
只是进入该计划的一小段时间。
我为存储数组而考虑的选项:
将它们存储为基数为long
。这仅适用于最多24(24.3717)个数字,因为long
不能超过2^63
位。< / p>
将它们存储为String
(例如{{0, 0, 0, 1}, {3, 6, 2, 0}}
将成为"00013620"
)。这只占用了4倍的空间(我认为),因为char仍然是1个字节。
使用BitSet
或BigInteger
之类的内容?我不知道每个是什么或它们是如何工作的。
所以我的问题是: 从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指出的那样,我对HashSet
的使用导致了上述错误,但是,我仍然内存不足
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)
答案 0 :(得分:1)
如果您希望HashSet
仅保留唯一的int[][]
,并消除重复项,那将无效 - equals
和hashCode
实施{ {1}}(和所有数组)都是基于身份的。如果你依赖于唯一性来保持不同数组的数量很小,那就无法工作了;你将不得不将它们包装在一个实现正确int[][]
和hashCode
的类型中。
答案 1 :(得分:1)
你似乎在制造很多电路板,很难遵循,但似乎你基本上生成了大小为6X6的所有阵列的大部分,其中每个单元可以具有1,2,...的任何值, 6。
此类数组的数量为6 ^ 36~ = 10 ^ 28。
这意味着,即使每个数组只有一个字节(它不可能),你仍然需要10 ^ 16 TB才能保存所有数据。
我建议您寻找一种不包括明确生成所有可能数组的替代方案。
作为旁注,表示对象的最低位数是ceil(log_2(6^36)) = 94
,但要获得最佳结果需要做很多工作,而且我赢了。建议它。
答案 2 :(得分:1)
最简单但仍具有内存效率的方法是将每个数组存储为两个long
s,每个字段占用3位(总共3 * 36 = 108个有用位,开销为20未使用的位)。虽然理论上的限制小于此值,但您几乎肯定希望您的结构与字边界对齐,因此您并没有真正失去任何东西。你获胜的是,访问单个字段既简单又快速,只需要进行位屏蔽和移位操作。
我还会看看堆外存储选项,以消除所有对象开销。