Module 4 · Arrays & Dynamic Arrays

Arrays in Memory

Concept~6 min

What an array actually is

Picture a train of identical, permanently coupled cars sitting on a straight length of track, numbered 0, 1, 2, … from the engine back. Every car is the same length, so if you know the engine's position and a car's number, you know exactly how far down the track that car sits — no walking the platform counting cars, just multiply the car number by the car length. That single picture is almost the whole lesson: an array is one contiguous block of memory holding equal-sized slots, and that single sentence explains everything arrays are good and bad at.

Because the block is contiguous and slots are equal-sized, the address of slot i is pure arithmetic — exactly the "car number × car length" math above:

address(i) = base_address + i × slot_size

That's why arr[i] is O(1): the computer doesn't search for index 7 — it computes 7's address and goes there directly. No other position is touched. Compare a chain of linked nodes (Linked Lists module), where reaching item 7 means walking 7 hops: same "get the i-th item" request, completely different mechanics.

one contiguous blockidx 01000hidx 11008hidx 21010hidx 31018hidx 41020h

The costs, from the layout

Every array cost is a consequence of "contiguous, equal-sized, packed":

Complexity

OperationCostWhy
read / write arr[i]O(1)address arithmetic — base + i × size
append at end (capacity free)O(1)write into the next unused slot
insert at index iO(n − i)every element after i must shift right one slot — contiguity forbids gaps
delete at index iO(n − i)every element after i shifts left to close the gap
search unsortedO(n)no structure to exploit — must scan

The shifting cost is the one people forget. list.insert(0, x) / arr.unshift(x) reads as one line but moves the entire array — on the train, coupling a new car in right behind the engine means uncoupling every single car behind that point and rolling each one back one position to make room, because the cars are physically joined and nothing can occupy two positions at once. A loop doing n front-insertions costs 1 + 2 + ⋯ + n shifts — the triangular sum, n(n+1)/2 — which is Θ(n²): one of the most common accidental quadratics in real code.

Cache locality: the hidden second superpower

Modern CPUs don't fetch one value from RAM at a time — they pull a cache line (typically 64 bytes) into fast cache. Concretely: a 64-byte line holds sixteen 4-byte integers, so reading arr[0] pulls arr[0..15] in together — the next fifteen reads (15/16, ~94%) hit cache for free before the CPU fetches again. Scan in order and every fetch brings the next several elements along for free this way; the CPU even detects the pattern and prefetches ahead. Scanning a linked structure scattered across memory defeats both effects — every hop is a potential cache miss costing ~100× a cache hit.

This doesn't change any Big O class — a scan is O(n) either way — but it's a constant factor of 10–100× in real time, and it's why "array + index arithmetic" beats fancier structures in practice far more often than asymptotics alone predict. It's the difference between a security guard walking the length of one train, glancing into each coupled car in turn as they pass, versus checking cargo scattered across cars parked in different rail yards all over the city — same number of cars checked either way, wildly different amounts of walking. When two designs tie on paper, bet on the contiguous one.

What Python lists and JS arrays really are

Neither language gives you raw fixed-size arrays. list and Array are dynamic arrays: a contiguous block plus bookkeeping (current length, allocated capacity) and a growth policy. Two footnotes worth knowing:

  • Python lists store pointers to objects, not the objects themselves — contiguity applies to the pointer block. (True packed storage exists in array/numpy.)
  • JavaScript engines store arrays contiguously as long as you keep them dense and same-typed; writing far past the end or mixing types can demote them to hash-map mode. Keep arrays dense.

The next lesson builds the dynamic array from scratch — including the growth policy that keeps append O(1) amortized, which you proved in the Big O module.

Check yourself

3 questions

01

Why is reading arr[500000] O(1) rather than requiring a walk to position 500,000?

02

A loop builds a list by always inserting each new item at index 0. Total cost for n items?

03

Array scan and linked-list scan are both O(n). Why is the array scan often 10–100× faster in practice?