Adding elements to an array in Java requires working around the fixed length of arrays by creating a new, larger array and copying existing elements. This evergreen explainer covers reliable approaches using System.arraycopy, java.util.Arrays.copyOf, and when to prefer ArrayList or other collections instead. You will learn step-by-step patterns, performance implications, and best practices for safely increasing capacity while preserving ordering and avoiding common pitfalls.
Why Arrays Have Fixed Length in Java
In Java, arrays are fixed-length data structures: the length is set at allocation and cannot change. This design gives predictable O(1) indexed access but makes direct addition impossible. To add an element, you must create a new array with a larger size, copy the old contents, and then add the new element. Understanding this constraint clarifies why utility methods and collection classes exist to manage growth safely.
Manual Array Resizing Patterns
Using System.arraycopy for Copying
The core primitive for copying segments of arrays is System.arraycopy. It copies a range of elements from a source array into a destination array at a given offset, performs bounds checks, and handles overlapping regions safely. To add an element, allocate a new array with length + 1, call arraycopy to transfer existing elements, then write the new element at the desired index.
int[] original = {1, 2, 3};
int[] resized = new int[original.length + 1];
System.arraycopy(original, 0, resized, 0, original.length);
resized[resized.length - 1] = 4;
Using Arrays.copyOf for Simpler Growth
java.util.Arrays.copyOf returns a new array with the specified length, copying elements from the original up to Math.min(original.length, newLength). It simplifies resizing because you do not manually specify source position or destination position. To append, pass original.length + 1 as the new length, then assign the last index.
int[] original = {1, 2, 3};
original = Arrays.copyOf(original, original.length + 1);
original[original.length - 1] = 4;
Inserting at an Arbitrary Index
Adding at index i requires shifting elements i..(length-1) one position to the right. Use System.arraycopy with source position i and destination position i + 1, specifying length - i elements to move. Then assign the new value at index i. Always validate that 0 ≤ i ≤ length to avoid ArrayIndexOutOfBoundsException.
Performance and Memory Considerations
Each manual resize allocates a new array and copies all elements, costing O(n) time and additional O(n) space. Repeated single-element growth leads to O(n²) total time if performed in a loop. Mitigate this by over-allocating capacity (for example, increasing by a factor such as 1.5 or 2) or using a collection that manages amortized growth. Benchmark when performance is critical, since large copies can affect latency-sensitive code.
When to Use ArrayList Instead
For most use cases requiring frequent additions, java.util.ArrayList is the pragmatic choice. It internally uses resizing arrays and handles copying, capacity planning, and bounds checking for you. You can still access the underlying array via toArray when needed, but in-place mutation is cleaner and less error-prone. Reserve manual array resizing for low-latency contexts or when integrating with APIs that require a specific array type.
Common Pitfalls and Safe Practices
- Validate indices before shifting to avoid negative shifts or out-of-bounds writes.
- Prefer Arrays.copyOf over manual new int[size] plus arraycopy for brevity and readability.
- Avoid growing arrays one element at a time in performance-sensitive loops.
- Remember that toArray(new String[0]) on collections incurs an extra allocation; prefer toArray(new String[size]) when size is known.
- Use System.arraycopy with caution regarding source and destination overlap; for non-overlapping regions it is safe and efficient.