在 Java 中有效地删除数组列表中每个数组的第一个元素
Efficiently removing the first element of every array inside an arraylist in Java
我想从存储在 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'
问题是如何使这个 运行 对于非常大的列表更有效。
任何类型的提示将不胜感激。
Java 中的数组大小无法更改。因此,您不能从技术上删除数组中的元素。您基本上需要构造一个新数组,然后从原始数组复制。这正是你所做的。
对于复制,Arrays.copyOfRange(oldArr, 1, oldArr.length)
可能是最简单的方法。试试看有没有变化。
我想你还有一些其他的选择:
增加堆(以-Xmx1024m为例)
重新考虑你的double[][]输入?有什么解决办法吗?
除了 运行将程序安装在具有更多 RAM 的机器上(如果您已经为 JVM 提供了您拥有的一切)之外,您的问题可能没有解决方案。
您的程序将为输入分配 x 字节的堆,然后为更正后的列表分配几乎另一个 x 字节的堆。 OutOfMemoryError
告诉您,JVM 无法分配更多内存,这包括尝试释放由未使用的引用分配的内存(GC 工作)。
OutOfMemoryError Java8 SE
Thrown when the Java Virtual Machine cannot allocate an object because it is out of memory, and no more memory could be made available by the garbage collector.
事实是,当您将它们存储在输出列表中时,您的程序中没有未使用的引用。这意味着,机器 运行 宁此代码必须有 sizeOfInput*2 字节 RAM 不 运行 进入 OutOfMemoryError
附带说明:
看到您不需要二维,因为所有数组初始值设定项都使用 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
.
由于您不需要保留输入列表并且您有足够的内存用于它,所以这个应该适合您。无需创建新对象,只需更改当前对象即可。对于每个外部数组,通过跳过第一个元素然后设置新元素来创建它的副本。
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));
}
}
我想从存储在 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'
问题是如何使这个 运行 对于非常大的列表更有效。
任何类型的提示将不胜感激。
Java 中的数组大小无法更改。因此,您不能从技术上删除数组中的元素。您基本上需要构造一个新数组,然后从原始数组复制。这正是你所做的。
对于复制,Arrays.copyOfRange(oldArr, 1, oldArr.length)
可能是最简单的方法。试试看有没有变化。
我想你还有一些其他的选择:
增加堆(以-Xmx1024m为例)
重新考虑你的double[][]输入?有什么解决办法吗?
除了 运行将程序安装在具有更多 RAM 的机器上(如果您已经为 JVM 提供了您拥有的一切)之外,您的问题可能没有解决方案。
您的程序将为输入分配 x 字节的堆,然后为更正后的列表分配几乎另一个 x 字节的堆。 OutOfMemoryError
告诉您,JVM 无法分配更多内存,这包括尝试释放由未使用的引用分配的内存(GC 工作)。
OutOfMemoryError Java8 SE
Thrown when the Java Virtual Machine cannot allocate an object because it is out of memory, and no more memory could be made available by the garbage collector.
事实是,当您将它们存储在输出列表中时,您的程序中没有未使用的引用。这意味着,机器 运行 宁此代码必须有 sizeOfInput*2 字节 RAM 不 运行 进入 OutOfMemoryError
附带说明:
看到您不需要二维,因为所有数组初始值设定项都使用 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
.
由于您不需要保留输入列表并且您有足够的内存用于它,所以这个应该适合您。无需创建新对象,只需更改当前对象即可。对于每个外部数组,通过跳过第一个元素然后设置新元素来创建它的副本。
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));
}
}