【问题标题】:Efficiently removing the first element of every array inside an arraylist in Java有效地删除Java中arraylist中每个数组的第一个元素
【发布时间】:2018-08-16 15:32:23
【问题描述】:

我想从存储在ArrayList<double[][]> 中的每个double[][] 数组中删除第一个元素。该列表存储了大量数据(60000 个元素)。

我尝试使用以下代码实现此目的:

public ArrayList<double[][]> correctInput(ArrayList<double[][]> input) {
    double[][] temp;
    ArrayList<double[][]> correctedinput = new ArrayList<>();

    for (int n = 0; n < input.size(); n++) {
        temp = new double[input.get(0).length - 1][1];

        for (int i = input.get(n).length - 1; i > 0; i--) {
            temp[i - 1][0] = input.get(n)[i][0];
        }

        correctedinput.add(temp);
    }

    return correctedinput;
}

虽然我的代码适用于较小的列表,但当尝试在大列表中使用它时会导致

java.lang.OutOfMemoryError: GC Overhead Limit Exceeded

可能是因为我正在为列表的每个元素重新定义双精度数组(名为 temp)。 还有一件事:列表中的数组被用作向量,这就是为什么它们是二维数组,尽管它们的长度只有 1。

'temp[0].lenght = 1'

问题是我如何让这个运行更有效地处理非常大的列表。
任何形式的提示将不胜感激。

【问题讨论】:

  • 创建新的 ArrayList 后真的需要保持原 ArrayList 不变吗?也许没有足够的空间让两个 ArrayList 一起存在。
  • 可能是这样。 ArrayList 不需要保持不变。
  • 您能提供一些适当的输入吗?
  • 这有点问题,因为它必须在大小为 60000 的 ArrayList 上工作,但以下将导致一个与 60000 非常相似(尽管更小)的 Array。 'for (int i = 0; i
  • 保留输入重要吗?随机访问输入重要吗?如果没有,请考虑使用 LinkedList 或 Deque 或类似的,因为它们已针对 O(1) 中的头/尾移除进行了优化。它还将避免您必须为结果创建另一个大数组的情况。

标签: java arrays arraylist out-of-memory


【解决方案1】:

试试这个方法:

Java 8+

public static <T> void remove1stArrayElementFromEveryElement(List<T[]> list) {
    list.replaceAll(array -> Arrays.copyOfRange(array, 1, array.length));
}

Java 7+

public static <T> void remove1stArrayElementFromEveryElement(List<T[]> list) {
    for (ListIterator<T[]> iter = list.listIterator(); iter.hasNext(); ) {
        T[] array = iter.next();
        array = Arrays.copyOfRange(array, 1, array.length);
        iter.set(array);
    }
}

我已将其设为通用,因此它与double[][]int[][] 同样适用。这是一个 void 方法,因为它直接修改传递的列表,而不进行复制。它制作的唯一副本是列表中每个数组的临时副本。此外,它适用于任何列表,而不仅仅是ArrayList

【讨论】:

  • 虽然这看起来很完美,但我无法让它工作,因为 Eclipse 告诉我它不适用于“ArrayList”。抱歉,我对泛型方法非常缺乏经验。
  • @Z3R0 这很奇怪,当我通过ArrayList&lt;double[][]&gt; 时它工作正常。你能给我确切的错误信息吗?如果您直接调用list.replaceAll(如上面我的sn-p)而不将其包装在此方法中会怎样?
  • 是的!效果很好。谢谢 看来我必须学习 lambda 表达式的方式
【解决方案2】:

Java 中数组的大小无法更改。因此,从技术上讲,您不能从数组中删除元素。您基本上需要构造一个新数组,然后从原始数组中复制。这正是你所做的。

对于复制,Arrays.copyOfRange(oldArr, 1, oldArr.length) 可能是最简单的方法。试试看有什么变化。

我认为您在这里还有其他选择:

  1. 增加堆(以 -Xmx1024m 为例)

  2. 重新考虑你的 double[][] 输入?有什么解决办法吗?

【讨论】:

  • 我可以使用 int[][] 作为输入,但我不确定这是否有帮助。
  • 是的,这样更好。 int 消耗的内存比 double 少。
  • 我必须纠正自己,虽然可以使用 int[][] 这意味着我以后必须将它从 int[][] 转换为 double[][] 或者我必须更改我几乎所有的项目类,这意味着我宁愿不这样做。
【解决方案3】:

由于您不需要保留输入列表,并且您有足够的内存来存储它,因此这个列表应该适合您。无需创建新对象,只需更改当前对象即可。对于每个外部数组,通过跳过第一个元素然后设置新元素来创建它的副本。

public static void correctInputVoid(ArrayList<double[][]> input) {
    for (ListIterator<double[][]> listIterator = input.listIterator(); listIterator.hasNext(); ) {
        double[][] next = listIterator.next();
        listIterator.set(Arrays.copyOfRange(next, 1, next.length));
    }
}

【讨论】:

    【解决方案4】:

    除了在具有更多 RAM 的机器上运行程序(如果你已经为 JVM 提供了你所拥有的一切)之外,你的问题可能没有解决方案。

    您的程序将为输入分配 x 字节的堆,然后为更正的列表分配几乎另一个 x。 OutOfMemoryError 告诉您,JVM 无法分配更多内存,这包括尝试释放由未使用的引用分配的内存(GC 工作)。

    OutOfMemoryErrorJava8 SE

    当 Java 虚拟机因为内存不足而无法分配对象时抛出,并且垃圾收集器无法提供更多内存。

    问题是,您的程序中没有未使用的引用,因为您将它们存储在输出列表中。这意味着,运行此代码的机器必须有 sizeOfInput*2 字节 RAM 才能运行到 OutOfMemoryError

    附带说明: 看到您不需要第二维,因为所有数组初始值设定项都使用 1 作为第二维,所以请改用一维数组。仅此一项就可以极大地提升性能。

    【讨论】:

    • 好的,我担心稍后在尝试从建议的一维数组转换为二维数组时可能会再次遇到相同的错误,但我会尝试这样做,因为我没有任何内存分配。
    • 如果不需要一次性处理所有内容,请按顺序进行。读取一些输入,直到达到上限(例如,由命令行参数指定),处理它,输出它(可能到磁盘上的文件?)然后重复直到完成。
    猜你喜欢
    • 1970-01-01
    • 2020-12-04
    • 2021-05-15
    • 1970-01-01
    • 2017-02-25
    • 1970-01-01
    • 2019-02-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多