在没有额外内存的情况下将集合转换为数组
Converting collection to array with no extra memory
我需要将Map转换为二维数组,所以我写了这段代码,但它占用了大量内存,我不明白为什么。
private DataItem[][] convertDataToArrays(boolean[] filter,
Map<Integer, List<T>> dataSet)
double[] data = new double[sizeOfNewVector];
DataItem[][] reducedData = new DataItem[dataSet.size()][];
for (int i = dataSet.size() - 1; i >= 0; i--) {
reducedData[i] = new DataItem[dataSet.get(i).size()];
for (int j = reducedData[i].length - 1; j >= 0; j--) {
reducedData[i][(reducedData[i].length - 1) - j] = new DataItem(data);
dataSet.get(i).remove(j);
}
dataSet.remove(i);
}
return reducedData;
这里是数据项class:
public class DataItem {
public double[] data;
public DataItem(double[] data) {
this.data = new double[data.length];
System.arraycopy(data, 0, this.data, 0, data.length);
}
}
算法应该做什么:
- 从列表中取出最后一个元素
- 复制它。
- 从列表中删除元素
- 将副本存储到新的二维数组中
- 重复直到列表为空
这应该适用于地图中的所有列表。
问题是,第 3 步只留下元素,并没有缩小数组,所以当我在 convert 方法中插入一个巨大的数据集时,我得到了 java.lang.OutOfMemoryError: GC overhead limit exceeded
我需要在没有任何额外内存的情况下完成它。谁能帮帮我?
编辑:
我正在使用 ArrayList 和 HashMap。
你的理论完全有可能。缩小用于存储引用的内部数组的大小确实需要 ArrayList
一段时间。您可以通过使用另一个 List
实现来避免这种影响,例如 LinkedList
,它不会显示这种行为,但它们也有相当大的内存开销,可能会耗尽您保存的 space。
话虽如此,鉴于您的数据结构,我发现仅 ArrayList
中一些额外引用的开销就不太可能将您的内存需求推到最高点。我发现您更有可能创建所有显然相对较大(从内部数组判断)类型 DataItem
的对象的副本。如果其他人仍然有对原始 DataItem
对象的引用,您对 remove
的调用将从列表中删除他们的 references,但是 objects他们自己 一直存在,直到 所有 对他们的引用被删除。
我建议检查您的内存占用情况,使用一个较小的实际可行的示例,使用 MAT tool 之类的东西。查看转换前 和转换后 有多少类型 DataItem
的对象。如果它们增加了,我的理论是正确的,你应该通过不复制 objects 而只复制它们的 references 来避免这个问题(如果可以的话) ,或者通过摆脱对旧对象的额外引用。如果我的理论是错误的,请检查内存的哪一部分增加最多以找出罪魁祸首。
我需要将Map转换为二维数组,所以我写了这段代码,但它占用了大量内存,我不明白为什么。
private DataItem[][] convertDataToArrays(boolean[] filter,
Map<Integer, List<T>> dataSet)
double[] data = new double[sizeOfNewVector];
DataItem[][] reducedData = new DataItem[dataSet.size()][];
for (int i = dataSet.size() - 1; i >= 0; i--) {
reducedData[i] = new DataItem[dataSet.get(i).size()];
for (int j = reducedData[i].length - 1; j >= 0; j--) {
reducedData[i][(reducedData[i].length - 1) - j] = new DataItem(data);
dataSet.get(i).remove(j);
}
dataSet.remove(i);
}
return reducedData;
这里是数据项class:
public class DataItem {
public double[] data;
public DataItem(double[] data) {
this.data = new double[data.length];
System.arraycopy(data, 0, this.data, 0, data.length);
}
}
算法应该做什么:
- 从列表中取出最后一个元素
- 复制它。
- 从列表中删除元素
- 将副本存储到新的二维数组中
- 重复直到列表为空
这应该适用于地图中的所有列表。
问题是,第 3 步只留下元素,并没有缩小数组,所以当我在 convert 方法中插入一个巨大的数据集时,我得到了 java.lang.OutOfMemoryError: GC overhead limit exceeded
我需要在没有任何额外内存的情况下完成它。谁能帮帮我?
编辑:
我正在使用 ArrayList 和 HashMap。
你的理论完全有可能。缩小用于存储引用的内部数组的大小确实需要 ArrayList
一段时间。您可以通过使用另一个 List
实现来避免这种影响,例如 LinkedList
,它不会显示这种行为,但它们也有相当大的内存开销,可能会耗尽您保存的 space。
话虽如此,鉴于您的数据结构,我发现仅 ArrayList
中一些额外引用的开销就不太可能将您的内存需求推到最高点。我发现您更有可能创建所有显然相对较大(从内部数组判断)类型 DataItem
的对象的副本。如果其他人仍然有对原始 DataItem
对象的引用,您对 remove
的调用将从列表中删除他们的 references,但是 objects他们自己 一直存在,直到 所有 对他们的引用被删除。
我建议检查您的内存占用情况,使用一个较小的实际可行的示例,使用 MAT tool 之类的东西。查看转换前 和转换后 有多少类型 DataItem
的对象。如果它们增加了,我的理论是正确的,你应该通过不复制 objects 而只复制它们的 references 来避免这个问题(如果可以的话) ,或者通过摆脱对旧对象的额外引用。如果我的理论是错误的,请检查内存的哪一部分增加最多以找出罪魁祸首。