在没有额外内存的情况下将集合转换为数组

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);
    }
}

算法应该做什么:

  1. 从列表中取出最后一个元素
  2. 复制它。
  3. 从列表中删除元素
  4. 将副本存储到新的二维数组中
  5. 重复直到列表为空

这应该适用于地图中的所有列表。

问题是,第 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 来避免这个问题(如果可以的话) ,或者通过摆脱对旧对象的额外引用。如果我的理论是错误的,请检查内存的哪一部分增加最多以找出罪魁祸首。