将集合转换为数组而没有额外的内存

时间:2015-06-14 14:43:00

标签: java arrays performance memory collections

我需要将Map转换为2D数组,所以我已经编写了这段代码,但它占用了大量内存,我无法弄清楚原因。

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;

这里是DataItem类:

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. 将副本存储到新的2D数组
  5. 重复直到列表为空
  6. 这应该适用于地图中的所有列表。

    问题是,第3步。只留下元素并且不缩小数组,所以当我在convert方法中插入一个庞大的数据集时,我得到了java.lang.OutOfMemoryError:超出了GC开销限制

    我需要在没有任何额外记忆的情况下做到这一点。有人可以帮帮我吗?

    编辑:

    我正在使用ArrayList和HashMap。

1 个答案:

答案 0 :(得分:1)

你的理论完全有可能。它需要ArrayList一段时间来缩小用于存储引用的内部数组的大小。您可以通过使用其他List实现来避免这种影响,例如LinkedList但不会显示此行为,但这些实现也会产生相当大的内存开销,可能会占用您保存的空间。

话虽如此,鉴于您的数据结构,我发现ArrayList中的一些额外引用的开销几乎不可能将您的内存需求推到顶部。我发现你更有可能创建所有类型的副本,显然相对较大(从内部数组来看),DataItem类型的对象。如果其他人仍然引用原始DataItem个对象,则您对remove的调用将从列表中删除其引用,但对象本身保持活着,直到所有对它们的引用都被删除。

我建议使用类似MAT tool之类的更小的示例检查您的内存占用情况。在转化和转换之后,查看类型中有多少DataItem个对象。如果它们增加了,我的理论是正确的,您应该通过不复制对象,而只是复制引用(如果可以)或者通过删除来避免这个问题对旧对象的附加引用。如果我的理论错了,请检查记忆的哪一部分增加最多,以确定罪魁祸首。