[java] How to increase an array's length

I have a quick question. I have an integer array in java that needs its length to vary throughout the class. Specifically, I need it to increase by one in certain situations. I tried it like this.

        sbgHeadX = new int[ numberOfSBG ];

I would increase the integer variable numberOfSBG when I needed to, but I don't think this works. Is there any other way?

This question is related to java arrays

The answer is


First things first:

  • In Java, once an array is created, it's length is fixed. Arrays cannot be resized.
  • You can copy the elements of an array to a new array with a different size. The easiest way to do this, is to use one of the Arrays.copyOf() methods.
  • If you need a collection of variable size, you're probably better off using an ArrayList instead of an array.

That being said, there might be situations where you have no other choice than to change the size of an array that is created somewhere outside of your code.1 The only way to do that is to manipulate the generated bytecode of the code that creates the array.

Proof-of-concept

Below is a small proof-of-concept project that uses Java instrumentation to dynamically change the size of an array2. The sample project is a maven project with the following structure:

.
+- pom.xml
+- src
   +- main
      +- java
         +- com
            +- stackoverflow
               +- agent
                  +- Agent.java
                  +- test
                     +- Main.java

Main.java

This file contains the target class of which we're going to manipulate the 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";
    }
}

In the main method, we create a String array of size 1. In the fun method, 4 additional values are assigned outside of the array's bounds. Running this code as-is will obviously result in an error.

Agent.java

This file contains the class that will perform the bytecode manipulation:

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;
            }
        });
    }
}

On the bytecode level, the creation of the String array in the Main class consists of two commands:

  • iconst_1, which pushes an int constant with value 1 onto the stack (0x04).
  • anewarray, which pops the value of the stack and creates a reference array3 of the same size (0xbd). The above code looks for that combination of commands in the Main class, and if found, replaces the const_1 command with a const_5 command (0x08), effectively changing the dimensions of the array to 5.4

pom.xml

The maven POM file is used to build the application JAR and configure the main class and the Java agent class.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>

Build and execute

The sample project can be built using the standard mvn clean package command.

Executing without referencing the agent code will yield the expected error:

$> 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)

While executing with the agent code will yield:

$> java -javaagent:target/agent.jar -jar target/agent.jar
[Zero, One, Two, Three, Four]

This demonstrates that the size of the array was successfully changed using bytecode manipulation.


1 Such situations came up in questions here and here, the latter of which prompted me to write this answer.
2 Technically, the sample project doesn't resize the array. It just creates it with a different size than the size specified in code. Actually resizing an existing array while maintaining its reference and copying its elements would be a fair bit more complicated.
3 For a primitive array, the corresponding bytecode operation would be newarray (0xbc) instead.
4 As noted, this is just a proof of concept (and a very hacky one at that). Instead of randomly replacing bytes, a more robust implementation could use a bytecode manipulation library like ASM to insert a pop command followed by an sipush command before any newarray or anewarray command. Some more hints towards that solution can be found in the comments to this answer.
5 In a real-world scenario, the agent code would obviously be in a separate project.


If you don't want or cannot use ArrayList, then there is a utility method:

Arrays.copyOf() 

that will allow you to specify new size, while preserving the elements.


You can't increase the array's length if it is not declared in heap memory (see below code which first array input is asked by user and then it asks how much you want to increase array and also copy previous array elements):

#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);
}

You can make use of ArrayList. Array has the fixed number of size.

This Example here can help you. The example is pretty easy with its output.

Output:
2 5 1 23 14 
New length: 20
Element at Index 5:29
List size: 6
Removing element at index 2: 1
2 5 23 14 29

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;

By definition arrays are fixed size. You can use instead an Arraylist wich is that, a "dynamic size" array. Actually what happens is that the VM "adjust the size"* of the array exposed by the ArrayList.

See also

*using back-copy arrays


Example of Array length change method (with old data coping):

static int[] arrayLengthChange(int[] arr, int newLength) {
    int[] arrNew = new int[newLength];
    System.arraycopy(arr, 0, arrNew, 0, arr.length);
    return arrNew;
}

Arrays in Java are of fixed size that is specified when they are declared. To increase the size of the array you have to create a new array with a larger size and copy all of the old values into the new array.

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));

Alternatively you could use a dynamic data structure like a List.