簡単な質問があります。私はJavaに整数配列を持っていますが、その長さはクラス全体で変化する必要があります。具体的には、状況によっては1つずつ増やす必要があります。こんな感じでやってみました。
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";
}
}
このメソッドでは、サイズ1main
の配列を作成します。このメソッドでは、配列の境界外に4つの追加の値が割り当てられます。このコードをそのまま実行すると、明らかにエラーが発生します。String
fun
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
は2つのコマンドで構成されます。
iconst_1
int
、値1の定数をスタック( )にプッシュします0x04
。anewarray
、スタックの値をポップし、同じサイズの参照配列30xbd
を作成します( )。上記のコードは、クラス内のコマンドの組み合わせを探し、見つかった場合は、コマンドをコマンド( )Main
に置き換えて、配列の次元を5に効果的に変更します。4const_1
const_5
0x08
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
(0xbc
)になります。
4すでに述べたように、これは概念実証にすぎません(そして非常にハッキーなものです)。バイトをランダムに置き換える代わりに、より堅牢な実装では、 ASMのようなバイトコード操作ライブラリを使用できます。またはコマンドの前にコマンドを挿入し、pop
その後にコマンドを挿入します。その解決策に向けたいくつかのヒントは、この回答へのコメントにあります。5実際のシナリオでは、エージェントコードは明らかに別のプロジェクトにあります。sipush
newarray
anewarray
ArrayListを利用できます。配列のサイズは固定数です。
ここでのこの例はあなたを助けることができます。この例は、出力が非常に簡単です。
出力: 2 5 1 23 14
新しい長さ:20
インデックス5:29の要素
リストサイズ:6
インデックス2の要素の削除:1
2 5 23 14 29
定義上、配列は固定サイズです。代わりに、「動的サイズ」配列であるArraylistを使用できます。実際に何が起こるかというと、VMはArrayListによって公開される配列の「サイズを調整」*します。
*バックコピーアレイを使用
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;
配列の長さを変更する方法の例(古いデータの対処方法を使用):
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);
}
「ArrayListを使用する」などのことを言わずに質問に答えます。これは、特に最高のパフォーマンスが必要な場合は、常にオプションであるとは限りません。
これを行うためのネイティブな方法はありませんが、次のようなものを使用できます。
int[] newArray = new int[oldArray.length + 1];
System.arrayCopy(oldArray, 0, newArray, 0, oldArray.length);
oldArray = newArray;
これで、最後の位置は自由に使用できます。「1」は任意の数字に変更できることに注意してください。繰り返し使用する場合は、このコードからメソッドを作成することもできます。