HOWTO · Java
How to Create a Dynamic Array in Java
This guide teaches how to create a dynamic array in Java.
On this page
Java arrays have a fixed length after creation. To add elements beyond that length, wrap a regular array in a dynamic array class that tracks how many values are stored and replaces its backing array when it runs out of room. The example below stores primitive int values, appends at the end, removes the last value, and can shrink its storage to match its current size.
Create a Dynamic Array using Custom Logic in Java
The class owns three pieces of state: the backing int[], size, and capacity. The size is the number of values currently stored; capacity is the backing array’s length. Keeping these values separate lets the array reserve slots without treating them as stored values.
Run this example:
import java.util.Arrays;
public class DynamicArray {
private int[] values;
private int size;
public DynamicArray() {
values = new int[2];
}
public void add(int value) {
if (size == values.length) {
int newCapacity = values.length == 0 ? 1 : values.length * 2;
values = Arrays.copyOf(values, newCapacity);
}
values[size] = value;
size++;
}
public void removeLast() {
if (size == 0) {
return;
}
size--;
values[size] = 0;
}
public void shrinkToSize() {
values = Arrays.copyOf(values, size);
}
public int size() {
return size;
}
public int capacity() {
return values.length;
}
@Override
public String toString() {
return Arrays.toString(Arrays.copyOf(values, size));
}
public static void main(String[] args) {
DynamicArray numbers = new DynamicArray();
numbers.add(10);
numbers.add(20);
numbers.add(30);
numbers.add(40);
numbers.add(50);
System.out.println(numbers + " | size=" + numbers.size()
+ " | capacity=" + numbers.capacity());
numbers.removeLast();
System.out.println(numbers + " | size=" + numbers.size()
+ " | capacity=" + numbers.capacity());
numbers.shrinkToSize();
System.out.println(numbers + " | size=" + numbers.size()
+ " | capacity=" + numbers.capacity());
}
}
Output:
[10, 20, 30, 40, 50] | size=5 | capacity=8
[10, 20, 30, 40] | size=4 | capacity=8
[10, 20, 30, 40] | size=4 | capacity=4
After five additions, the values are [10, 20, 30, 40, 50], with size 5 and capacity 8. After removeLast(), the values are [10, 20, 30, 40], with size 4 and capacity 8. After shrinkToSize(), size and capacity are both 4.
Understand Size, Capacity, and Resizing
The constructor starts with capacity 2 and size 0. Each call to add checks whether size has reached capacity. When the backing array is full, Arrays.copyOf allocates a new array twice as long and copies the existing values into it. The next value is then written at index size, and size increases by one.
For the five additions shown, capacity changes from 2 to 4 and then to 8. The growth steps copy stored values, while the new array’s spare slots are available for later additions. Capacity describes allocated storage, not the number of values. The toString() method copies only the portion before size, so spare slots do not appear as elements.
Copying takes time proportional to the number of values copied and temporarily requires both arrays to exist. Doubling avoids a copy on every append, though an individual append that triggers growth still performs a copy. This small class is intended to demonstrate the mechanism; Java’s ArrayList<Integer> is usually a better choice for general-purpose collections.
Add and Remove Elements
add supports appending only; it does not insert at an arbitrary index. removeLast reduces size by one and clears the vacated slot to Java’s default int value, 0. Because int is a primitive value rather than a reference, clearing the slot is not needed for garbage collection; it simply keeps the unused part of the backing array predictable. Calling removeLast when size is 0 leaves the object unchanged.
Removal does not automatically shrink capacity. Call shrinkToSize() when reclaiming spare array slots matters more than preserving room for upcoming additions. Shrinking also copies values into a replacement array. If size is 0, capacity becomes 0; the next add grows it to 1 before storing the new value. The implementation stores only primitive int values and does not provide indexed access, insertion, or removal from the middle.