Skip to main content

Why is appending to the end of an array more efficient?

Short answer

Appending to the end of a dynamic array usually takes amortized O(1), because it does not require shifting the existing elements: it is enough to write the value at index size and increment size. Insertions at the start or middle require shifting a large number of elements, which gives O(n) and worse cache locality. The rare memory reallocations as capacity grows make appending to the end "almost always" O(1).

Detailed breakdown

How a dynamic array is built

  • An array stores its elements in a contiguous memory region.
  • There are two numbers: size (how many elements there actually are) and capacity (how many elements can be stored without reallocating memory).
  • When capacity fills up, the array usually "grows" by a factor (for example, 1.5-2x): a new memory block is allocated, and all elements are copied into it.

What happens on an append to the end

  • If there is spare capacity: the element is written at index size, and size increases by 1.
  • If capacity is exhausted: one rare "expensive" reallocation step happens (realloc + copying all the elements), after which many cheap insertions follow again.
  • Result: amortized complexity O(1), an excellent cache profile (sequential writes), minimal data movement.

What happens on an insertion at the start/middle

  • Room must be freed for the new element, so every element to the right of the insertion position is shifted.
  • Shifting is O(n) copy/move operations, which uses the cache worse and can invalidate references/iterators.
  • Result: inserting at the start/middle is O(n) even with enough capacity.

Amortized complexity

The capacity growth strategy (usually geometric: ×1.5-2) guarantees that "expensive" reallocations happen rarely. Averaged over a sequence of m appends, the average cost of one append operation becomes constant: O(1) amortized.

Example in JavaScript

A simple illustration of the run time of different kinds of insertions (pick N to fit your environment, so the tab does not "freeze"):

const N = 100_000; // lower this if needed console.time('push end'); let a = []; for (let i = 0; i < N; i++) a.push(i); console.timeEnd('push end'); console.time('unshift begin'); let b = []; for (let i = 0; i < N; i++) b.unshift(i); console.timeEnd('unshift begin'); console.time('splice middle'); let c = []; for (let i = 0; i < N; i++) c.push(i); // Insert into the middle N/10 times, so it does not take too long for (let i = 0; i < N / 10; i++) c.splice(Math.floor(c.length / 2), 0, i); console.timeEnd('splice middle');

Expected: push runs noticeably faster than unshift/splice, because it does not require mass shifts.

Practical takeaways for a web developer

  • For queues and stacks, use operations that work with the end of the array: push/pop are the cheapest.
  • If you often need the "first element", avoid frequent unshift/shift. Use a double-ended queue (deque) built on two stacks instead:
class Deque { constructor() { this.left = []; this.right = []; } pushBack(x) { this.right.push(x); } pushFront(x) { this.left.push(x); } popFront() { if (!this.left.length) while (this.right.length) this.left.push(this.right.pop()); return this.left.pop(); } popBack() { if (!this.right.length) while (this.left.length) this.right.push(this.left.pop()); return this.right.pop(); } } // Amortized O(1) without mass array shifts

Exceptions and nuances

  • A TypedArray in JS has a fixed size: "appending to the end" is impossible without creating a new buffer and copying (O(n)).
  • Immutable updates (for example, [...arr, x] or arr.concat(x)) always create a new array and copy the elements, which is O(n). Here, "the efficiency of appending to the end" refers to mutable arrays.
  • JS engines optimize push/pop (a fast path). unshift/shift operations often require moving elements and can degrade the array's internal representation.
  • For very small arrays the difference may be unnoticeable, but as N grows the effect becomes significant.

Conclusion

Appending to the end of a dynamic array is more efficient because it does not require shifting the existing elements and uses contiguous memory, which gives amortized O(1) complexity and better cache locality. Inserting at the start or middle requires moving O(n) elements and is therefore significantly slower.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.