我有一个快速的问题。我在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));
或者,您可以使用动态数据结构,如列表。
第一件事:
Arrays.copyOf()
方法。ArrayList
而不是数组。话虽如此,在某些情况下,您可能别无选择,只能更改在代码之外某处创建的数组的大小。1唯一的方法是操作创建数组的代码的生成字节码。
下面是一个小型的概念验证项目,它使用 Java工具来动态更改数组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
方法中,我们创建了一个String
大小为 1 的数组。在该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;
}
});
}
}
在字节码层面,类中String
数组的创建Main
由两个命令组成:
iconst_1
,它将int
值为 1 的常量压入堆栈 ( 0x04
)。anewarray
,它弹出堆栈的值并创建一个相同大小的引用数组3 ( 0xbd
)。上面的代码在Main
类中查找该命令组合,如果找到,则将const_1
命令替换为const_5
命令 ( 0x08
),从而有效地将数组的维度更改为5。4 pom.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
( )。4如前所述,这只是一个概念证明(而且是一个非常老套的证明)。代替随机替换字节,更健壮的实现可以使用像ASM这样的字节码操作库0xbc
在任何或命令之前插入一个pop
命令,然后是一个命令。可以在对此答案的评论中找到有关该解决方案的更多提示。5在现实世界的场景中,代理代码显然会在一个单独的项目中。sipush
newarray
anewarray
您可以使用 ArrayList。数组具有固定数量的大小。
这个例子可以帮助你。该示例的输出非常简单。
输出: 2 5 1 23 14
新长度:20
索引 5:29 处的元素
列表大小:6
删除索引 2 处的元素:1
2 5 23 14 29
数组长度更改方法示例(旧数据处理):
static int[] arrayLengthChange(int[] arr, int newLength) {
int[] arrNew = new int[newLength];
System.arraycopy(arr, 0, arrNew, 0, arr.length);
return arrNew;
}
Item[] 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;
如果未在堆内存中声明数组,则无法增加数组的长度(请参见下面的代码,用户询问第一个数组输入,然后询问您要增加多少数组并复制先前的数组元素):
#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);
}
回答您的问题而不说“使用 ArrayList”之类的东西,这可能并不总是一种选择,特别是当您想要最佳性能时:
没有本地方法可以做到这一点,但你可以使用类似这样的东西:
int[] newArray = new int[oldArray.length + 1];
System.arrayCopy(oldArray, 0, newArray, 0, oldArray.length);
oldArray = newArray;
就是这样,最后一个位置可以免费使用。请注意,您可以将“1”更改为任何您喜欢的数字。如果您重复使用它,您也可以使用此代码创建一个方法。