我有一个简短的问题。在Java中,我有一个整数数组,需要在整个类中使其长度变化。具体来说,我需要在特定情况下将其增加一个。我尝试像这样。
sbgHeadX = new int[ numberOfSBG ];
我想在需要时增加整数变量numberOfSBG的值,但我觉得这样做行不通。是否还有其他方法?
如果你不想或不能使用ArrayList,那么有一个实用工具方法:
Arrays.copyOf()
这将允许您指定新的大小,同时保留元素。
Java中的数组大小在声明时被确定,是固定的。如果要增加数组的大小,你需要创建一个更大的新数组,并将所有旧值复制到新数组中。
例如:
char[] copyFrom = { 'a', 'b', 'c', 'd', 'e' };
char[] copyTo = new char[7];
System.out.println(Arrays.toString(copyFrom));
System.arraycopy(copyFrom, 0, copyTo, 0, copyFrom.length);
System.out.println(Arrays.toString(copyTo));
或者你可以使用像List这样的动态数据结构。
Arrays.copyOf()
会使代码更短、更易读。 - ToolmakerSteveyourArraylist.get(2)
相当于 yourClassicArray[2]
。 - talnicolas先说一下:
Arrays.copyOf()
方法将数组的元素复制到一个不同大小的新数组中。这是最简单的方法。ArrayList
而不是数组。话虽如此,在某些情况下,您可能别无选择,只能更改在代码外部创建的数组的大小。1这唯一的方法是操作创建数组的代码生成的字节码。
以下是一个小的概念验证项目,它使用 Java instrumentation 来动态更改数组的大小2。该示例项目是一个带有以下结构的 Maven 项目:
.
├─ pom.xml
└─ src
└─ main
└─ java
└─ com
└─ stackoverflow
└─ agent
├─ Agent.java
└─ test
└─ Main.java
Main.java
这个文件包含目标类,我们将要操作它的字节码:
package com.stackoverflow.agent.test;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
String[] array = {"Zero"};
fun(array);
System.out.println(Arrays.toString(array));
}
public static void fun(String[] array) {
array[1] = "One";
array[2] = "Two";
array[3] = "Three";
array[4] = "Four";
}
}
main
方法中,我们创建了一个大小为1的String
数组。在fun
方法中,4个额外的值被分配到数组范围之外。按照当前的代码运行显然会导致错误。
Agent.java
这个文件包含了将执行字节码操作的类:package com.stackoverflow.agent;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
public class Agent {
public static void premain(String args, Instrumentation instrumentation) {
instrumentation.addTransformer(new ClassFileTransformer() {
public byte[] transform(ClassLoader l, String name, Class<?> c,
ProtectionDomain d, byte[] b) {
if (name.equals("com/stackoverflow/agent/test/Main")) {
byte iconst1 = (byte) 0x04;
byte iconst5 = (byte) 0x08;
byte anewarray = (byte) 0xbd;
for (int i = 0; i <= b.length - 1; i++) {
if (b[i] == iconst1 && b[i + 1] == anewarray) {
b[i] = iconst5;
}
}
return b;
}
return null;
}
});
}
}
Main
类中创建 String
数组包含两个命令:
iconst_1
,将值为 1 的 int
常量推送到栈上 (0x04
)。anewarray
,弹出栈上的值并创建相同大小的引用数组3 (0xbd
)。
以上代码查找 Main
类中的这种命令组合,如果找到,则将 const_1
命令替换为 const_5
命令 (0x08
),从而有效地将数组的维度更改为 5。4pom.xml
Maven POM 文件用于构建应用程序 JAR 并配置主类和 Java 代理类。5
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.stackoverflow</groupId>
<artifactId>agent</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<archive>
<manifestEntries>
<Main-Class>com.stackoverflow.agent.test.Main</Main-Class>
<Premain-Class>com.stackoverflow.agent.Agent</Premain-Class>
<Agent-Class>com.stackoverflow.agent.Agent</Agent-Class>
<Can-Retransform-Classes>true</Can-Retransform-Classes>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>
可以使用标准命令mvn clean package
构建示例项目。
未引用代理代码进行执行将导致预期错误:
$> java -jar target/agent.jar
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 1
at com.stackoverflow.agent.test.Main.fun(Main.java:15)
at com.stackoverflow.agent.test.Main.main(Main.java:9)
$> java -javaagent:target/agent.jar -jar target/agent.jar
[Zero, One, Two, Three, Four]
1 在这里和这里的问题中出现了这样的情况,后者促使我写下这个答案。
2 技术上,示例项目并没有调整数组大小。它只是使用不同于代码中指定的大小创建了一个数组。实际上,在保持其引用并复制其元素的同时调整现有数组的大小会更加复杂。
3 对于原始数组,相应的字节码操作将是newarray
(0xbc
)。
4 如前所述,这只是一个概念验证(而且非常hacky)。一个更健壮的实现可以使用像ASM这样的字节码操作库,在任何newarray
或anewarray
命令之前插入一个pop
命令,然后是一个sipush
命令。有关该解决方案的一些更多提示可以在这个答案的评论中找到。
5 在实际场景中,代理代码显然会在单独的项目中。
ArrayList
类在普通的Java代码中完成了它...而且数组并没有被数组列表暴露出来...它是一个被数组列表隐藏的私有对象。 - Stephen CItem[] newItemList = new Item[itemList.length+1];
//for loop to go thorough the list one by one
for(int i=0; i< itemList.length;i++){
//value is stored here in the new list from the old one
newItemList[i]=itemList[i];
}
//all the values of the itemLists are stored in a bigger array named newItemList
itemList=newItemList;
数组长度变更方法示例(包括旧数据复制):
static int[] arrayLengthChange(int[] arr, int newLength) {
int[] arrNew = new int[newLength];
System.arraycopy(arr, 0, arrNew, 0, arr.length);
return arrNew;
}
如果数组不是在堆内存中声明的(请参见下面的代码,其中首先要求用户输入第一个数组,然后询问您想要增加多少个数组元素,并复制以前的数组元素),则无法增加数组的长度:
#include<stdio.h>
#include<stdlib.h>
int * increasesize(int * p,int * q,int x)
{
int i;
for(i=0;i<x;i++)
{
q[i]=p[i];
}
free(p);
p=q;
return p;
}
void display(int * q,int x)
{
int i;
for(i=0;i<x;i++)
{
printf("%d \n",q[i]);
}
}
int main()
{
int x,i;
printf("enter no of element to create array");
scanf("%d",&x);
int * p=(int *)malloc(x*sizeof(int));
printf("\n enter number in the array\n");
for(i=0;i<x;i++)
{
scanf("%d",&p[i]);
}
int y;
printf("\nenter the new size to create new size of array");
scanf("%d",&y);
int * q=(int *)malloc(y*sizeof(int));
display(increasesize(p,q,x),y);
free(q);
}
int[] newArray = new int[oldArray.length + 1];
System.arrayCopy(oldArray, 0, newArray, 0, oldArray.length);
oldArray = newArray;
就是这样,最后一个位置是可以自由使用的。请注意,您可以将“1”更改为任何您喜欢的数字。如果您经常使用此代码,还可以将其制作成方法。
.size()
返回可用元素数的假设。 - ToolmakerSteve