How can stdvector access elements with huge gaps between them?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
std::vector can access any valid element index in constant time because it stores elements contiguously in memory. The distance between logical indices does not matter to the access operation itself. What does matter is that all elements up to the highest used index must actually exist, which makes std::vector a poor fit for truly sparse data.
Why Access Is Still Fast
The core idea behind std::vector is contiguous storage. If data() points to the first element, then element i lives at a fixed offset from that base address.
Conceptually, vec[i] is just pointer arithmetic:
Because the address of element i is derived directly from the base address plus i * sizeof(T), the lookup does not depend on how far apart used indices are from each other.
The Real Cost Is Memory, Not Index Math
Suppose you want to store values only at indices 5 and 1_000_000. A vector can do that only if its size is at least 1_000_001.
Access is still constant time, but the vector had to allocate space for every element in between. That is why std::vector is dense storage, not sparse storage.
Huge Gaps Do Not Mean Hidden Compression
The vector does not "skip" unused positions or remember only the populated indices. If the highest index is large, capacity and size must cover that range. The intermediate elements are real objects with real storage.
That means huge gaps are fine only when:
- the element type is small
- the maximum index is still reasonable
- dense contiguous storage is what you actually want
If the data is sparse, you are paying memory cost for mostly empty slots.
When a Different Container Is Better
For sparse keys, containers such as std::unordered_map or std::map are usually a better match:
Now only the used indices are stored. You lose true contiguous layout, but you stop allocating millions of unused elements.
Another Subtle Point: Bounds Still Matter
vec[1'000'000] is only valid if the vector actually has that many elements. Accessing beyond size() is undefined behavior:
If safety matters, use at() while debugging:
at() checks bounds and throws an exception on invalid access.
A Good Mental Model
Think of a vector as a dynamic array, not as a dictionary keyed by integers. Integer index lookup is fast because the memory is laid out densely. That same density is exactly why large unused gaps become wasteful.
So the answer to the question is:
- it accesses distant indices quickly because indexing is pointer arithmetic
- it does not avoid allocating the gap between them
Common Pitfalls
The biggest pitfall is confusing fast random access with sparse storage. std::vector gives the first but not the second.
Another issue is resizing a vector to a very large maximum index just to store a few values. That may compile and run, but it can waste a huge amount of memory.
People also forget that out-of-range indexing is undefined behavior with operator[]. If the vector size does not cover the index, the program is already in trouble.
Summary
- '
std::vectorsupports constant-time indexing because elements are stored contiguously.' - Large gaps between used indices do not hurt access speed directly.
- But the vector still allocates storage for all elements up to the highest index.
- For sparse integer keys,
std::unordered_maporstd::mapis often a better fit. - Fast access and efficient sparse storage are different design goals.

