skip to content

How do Arrays.copyOf and Arrays.copyOfRange work, what happens at the boundaries, and is the copy deep or shallow?

level: middleimportance: should knowfreq 45%

answer

  1. copyOf(original, newLength): pad with defaults or truncate
  2. copyOfRange(from, to): half-open [from, to)
  3. to may exceed length (pads); from out of range throws
  4. Shallow copy: object references shared
  5. Built on System.arraycopy; clone() is fixed-length

basics

~20 s

Arrays.copyOf makes a new array of a chosen length, copying the original and padding with defaults (0/null/false) if longer, or cutting off if shorter. copyOfRange copies a slice [from, to). Both make a shallow copy: object elements are shared, not cloned.

solid answer

~50 s

Arrays.copyOf(original, newLength) returns a brand-new array of newLength. It copies elements from the start; if newLength is larger than the original, the extra slots get the type's default (0 for numbers, false for boolean, null for objects); if smaller, it truncates. Arrays.copyOfRange(original, from, to) copies the half-open range [from, to) into a new array of length to - from; to may exceed the original length, in which case the overflow is again default-padded, but from must be within [0, length] or you get ArrayIndexOutOfBoundsException, and from > to throws IllegalArgumentException. Crucially the copy is shallow: for an object array the new array holds the same element references, so the objects themselves are shared and mutating one is visible through both. These methods are the safe, readable way to resize or slice arrays (under the hood they use System.arraycopy).

code

java · 12 lines
java
int[] a = {1, 2, 3};
int[] bigger  = Arrays.copyOf(a, 5);          // {1, 2, 3, 0, 0}  (padded)
int[] smaller = Arrays.copyOf(a, 2);          // {1, 2}            (truncated)

int[] slice = Arrays.copyOfRange(a, 1, 3);    // {2, 3}  -> [1, 3)
int[] over  = Arrays.copyOfRange(a, 1, 5);    // {2, 3, 0, 0}  (to past end -> padded)

// Shallow copy: object elements are shared
StringBuilder[] src = { new StringBuilder("x") };
StringBuilder[] cpy = Arrays.copyOf(src, 1);
cpy[0].append("!");
System.out.println(src[0]); // "x!"  -> same object, mutation visible via both

go deeper

for a junior

Can use copyOf to grow or shrink an array and knows extra slots get default values.

for a middle

Explains copyOfRange's half-open range, the boundary/exception rules, and that the copy is shallow.

for a senior

Distinguishes copyOf/copyOfRange/clone/System.arraycopy, reasons about shallow vs deep for object arrays, and writes correct deep copies for nested arrays.

for a principal

Weighs array resizing patterns against growable collections (ArrayList) and immutable copies (List.copyOf), and guides on avoiding accidental shared-mutable state from shallow copies.

## Why copying matters Arrays are **fixed length**: you cannot grow `int[] a` in place. To 'resize' you create a new, bigger (or smaller) array and copy the elements over. `Arrays.copyOf` and `Arrays.copyOfRange` are the clean, type-safe helpers for exactly this; internally they delegate to the fast native `System.arraycopy`. ## Arrays.copyOf(original, newLength) Returns a **new array** of length `newLength`, of the same component type. It copies `original[0..min(length, newLength)-1]` into the new array. Then: - If `newLength > original.length`, the trailing slots are filled with the **default value** of the element type: `0` for numeric primitives, `false` for `boolean`, `''` for `char`, and `null` for reference types. - If `newLength < original.length`, the result is **truncated** to the first `newLength` elements. - `newLength` must be `>= 0`, else `NegativeArraySizeException`. There is also `copyOf(T[] original, int newLength, Class<? extends T[]> newType)` to change the array's runtime type. ## Arrays.copyOfRange(original, from, to) Copies the **half-open range** `[from, to)` — index `from` included, `to` excluded — into a new array of length `to - from`. Boundary rules: - `from` must satisfy `0 <= from <= original.length`; otherwise `ArrayIndexOutOfBoundsException`. - `from > to` throws `IllegalArgumentException`. - `to` **may be greater than** `original.length`; the portion beyond the original is **default-padded** (just like `copyOf`). So `copyOfRange` can both slice and extend. ## Shallow vs deep — the key subtlety Both methods make a **shallow copy**. For a primitive array (`int[]`), 'shallow' is moot — the values themselves are copied. But for an **object array** (`String[]`, `Point[]`), the new array contains the **same references** as the original. You get a new *array* (adding/removing slots in one doesn't affect the other), but the **elements are shared objects**. Mutating an object through one array is visible through the other; only the references were duplicated, not the pointed-to objects. To get a deep copy you must clone each element yourself. ## Relationship to clone() and System.arraycopy - `array.clone()` is another shallow copy but always the **same length** — `copyOf` is more flexible (resize) and reads clearer. - `System.arraycopy(src, srcPos, dest, destPos, len)` is the low-level primitive these methods build on; use it when you copy **into an existing** array. ## Common pitfalls 1. Forgetting the copy is **shallow** and assuming nested objects are isolated. 2. Off-by-one on the **half-open** `[from, to)` range. 3. Passing `from > original.length` (throws) versus `to > original.length` (allowed, pads). 4. Negative lengths (`NegativeArraySizeException`). ## Mental model `copyOf` = 'give me a resized clone of this array, padded with defaults or truncated'. `copyOfRange` = the same but for a `[from, to)` slice that may run past the end (then padded). In both, the *array* is new but the *objects inside* are shared.

  • What value fills the extra slots when you copyOf to a larger length?
    The element type's default: 0 for numeric primitives, false for boolean, '' for char, and null for reference types.
  • How do you make a truly independent (deep) copy of a 2D int array?
    copyOf/clone the outer array, then copy each inner row too, e.g. for each row do copy[i] = Arrays.copyOf(a[i], a[i].length). A single copyOf only duplicates the outer array's row references.

saying these in an interview costs you the question

  • Assuming copyOf deep-copies nested objects
  • Off-by-one: treating [from, to) as inclusive of to
  • Thinking from > length is allowed (it throws; only to may exceed)
  • Expecting an exception when newLength > original.length (it pads instead)

context