J'ai une petite question. J'ai un tableau entier en Java qui a besoin de sa longueur pour varier dans la classe. Plus précisément, il me faut une augmentation d'un dans certaines situations. J'ai essayé comme ça.
sbgHeadX = new int[ numberOfSBG ];
J'augmenterais la variable entière numberOfSBG lorsque j'en aurais besoin, mais je ne pense pas que cela fonctionne. Y-a t'il une autre possibilité?
Je suggérerais que vous utilisiez un ArrayList car vous n’aurez plus à vous soucier de la longueur. Une fois créé, vous ne pouvez pas modifier la taille d'un tableau:
Un tableau est un objet conteneur qui contient un nombre fixe de valeurs d'un seul type. La longueur d'un tableau est établie lors de sa création. Après création, sa longueur est fixée.
( La source )
Si vous ne voulez pas ou ne pouvez pas utiliser ArrayList, il existe une méthode utilitaire:
Arrays.copyOf()
cela vous permettra de spécifier une nouvelle taille tout en préservant les éléments.
Les tableaux en Java ont une taille fixe, spécifiée lors de leur déclaration. Pour augmenter la taille du tableau, vous devez créer un nouveau tableau avec une taille plus grande et copier toutes les anciennes valeurs dans le nouveau tableau.
ex:
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));
Vous pouvez également utiliser une structure de données dynamique telle qu'une liste.
Par définition, les tableaux ont une taille fixe. Vous pouvez utiliser à la place un Arraylist, un tableau de "taille dynamique". En réalité, la VM "ajuste la taille" * du tableau exposé par ArrayList.
* en utilisant des tableaux de back-copy
Vous pouvez utiliser ArrayList. Le tableau a le nombre fixe de taille.
Cet Exemple ici peut vous aider. L'exemple est assez facile avec sa sortie.
Sortie: 2 5 1 23 14
Nouvelle longueur: 20
Élément à l'Index 5:29
Taille de la liste: 6
Enlèvement d’élément à l’indice 2: 1
2 5 23 14 29
Exemple de méthode de changement de longueur de tableau (avec l'ancien traitement des données)
static int[] arrayLengthChange(int[] arr, int newLength) {
int[] arrNew = new int[newLength];
System.arraycopy(arr, 0, arrNew, 0, arr.length);
return arrNew;
}
Premières choses d'abord:
Arrays.copyOf()
.ArrayList
au lieu d'un tableau.Cela étant dit, il peut arriver que vous n'ayez d'autre choix que de changer la taille d'un tableau créé quelque part en dehors de votre code.1 La seule façon de faire est de manipuler le bytecode généré du code qui crée le tableau.
Vous trouverez ci-dessous un petit projet de démonstration de concept utilisant Java instrumentation pour modifier de manière dynamique la taille d'un tableau.2. Le projet exemple est un projet maven ayant la structure suivante:
.
├─ pom.xml
└─ src
└─ main
└─ Java
└─ com
└─ stackoverflow
└─ agent
├─ Agent.Java
└─ test
└─ Main.Java
Main.Java
Ce fichier contient la classe cible dont nous allons manipuler le bytecode:
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";
}
}
Dans la méthode main
, nous créons un tableau String
de taille 1. Dans la méthode fun
, 4 valeurs supplémentaires sont attribuées en dehors des limites du tableau. L'exécution de ce code en l'état entraînera évidemment une erreur.
Agent.Java
Ce fichier contient la classe qui effectuera la manipulation du bytecode:
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;
}
});
}
}
Au niveau du bytecode, la création du tableau String
dans la classe Main
consiste en deux commandes:
iconst_1
, qui insère une constante int
de valeur 1 dans la pile (0x04
).anewarray
, qui affiche la valeur de la pile et crée un tableau de référence3 de même taille (0xbd
) . Le code ci-dessus recherche cette combinaison de commandes dans la classe Main
et, le cas échéant, remplace la commande const_1
par une commande const_5
(0x08
), modifiant ainsi les dimensions du tableau en 5 .4pom.xml
Le fichier POM maven est utilisé pour créer le fichier JAR de l’application et pour configurer la classe principale et la classe de l’agent 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>
L'exemple de projet peut être construit à l'aide de la commande standard mvn clean package
.
L'exécution sans faire référence au code de l'agent produira l'erreur attendue:
$> 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)
Lors de l'exécution avec le code de l'agent, vous obtiendrez:
$> Java -javaagent:target/agent.jar -jar target/agent.jar
[Zero, One, Two, Three, Four]
Cela démontre que la taille du tableau a été modifiée avec succès à l'aide de la manipulation de code intermédiaire.
1 De telles situations sont apparues dans les questions ici et ici , lesquelles m'ont incité à écrire cette réponse.
2 Techniquement, l'exemple de projet ne redimensionne pas le tableau. Il le crée simplement avec une taille différente de celle spécifiée dans le code. Redimensionner un tableau existant tout en conservant sa référence et en copiant ses éléments serait un peu plus compliqué.
3 Pour un tableau primitif, l'opération de bytecode correspondante serait newarray
(0xbc
) à la place.
4 Comme indiqué, ceci est juste une preuve de concept (et une très hacky à cela). Au lieu de remplacer aléatoirement des octets, une implémentation plus robuste pourrait utiliser une bibliothèque de manipulation de code unique comme ASM pour insérer une commande pop
suivie d'une sipush
avant commande newarray
ou anewarray
. Quelques commentaires supplémentaires sur cette solution peuvent être trouvés dans les commentaires de cette réponse .
5 Dans un scénario réel, le code de l'agent serait évidemment dans un projet séparé.
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;