Concepts / Choosing a Container for Lookup

Choosing a Container for Lookup

The cost of a map lookup is $O(\log(n))$, where n is the number of elements, and the source notes that a map with 1,000,000 elements needs only about 20 comparisons and indirections.

  • Programming

A Lookup You Already Rely On

Suppose you already store your data in a map and you ask it to find one element. How much work does that search take? The answer depends on the container you chose, and choosing well is what this article is about. The source answers the question for a map directly: the cost of a map lookup is $O(\log(n))$, where $n$ is the number of elements in the map. The source calls this pretty good. The rest of this article explains why it is good, and when a different kind of container can do better.

What do you think happens?

A map holds 1,000,000 elements. Roughly how many comparisons and indirections does one lookup need?

  • About 1,000,000, since the map may have to inspect every entry

  • About 20

  • About 2

Reveal answer
Answer:

About 20

The source gives this figure directly: a map with 1,000,000 elements needs only about 20 comparisons and indirections to find an element. The map does not inspect a million entries.

Following a Map Lookup Step by Step

A map finds elements by comparison. It uses an ordering function, such as the less-than operator, to decide which way to move as it searches. At each step, the map compares the key it is looking for against an element it has reached, and the result of that comparison tells it where the element must be if it exists at all. Its search is therefore a sequence of decisions, each one narrowing the set of places that could still hold the answer.

Read the picture below from top to bottom. Each step starts with a candidate set of elements and ends with a smaller one. The number on each node is the count of candidates still in play. The diagram shows the idea of repeated narrowing: the search space shrinks at every comparison, so the number of steps needed is far smaller than the number of elements.

1,000,000candidatesstart of search500,000 candidatesafter 1 comparison250,000 candidatesafter 2 comparisons125,000 candidatesafter 3 comparisonsAbout 20 stepstotal for 1,000,000elementscomparecomparecomparecontinue narrowing
What happens at each step of a tree-based lookup, and how the remaining search space shrinks until a 1,000,000-element map needs only about 20 comparisons

What the Logarithm Means in Practice

A cost written as $O(\log(n))$ grows with the size of the collection, but much more slowly than the size itself. The source makes this concrete with a single example: for a map with 1,000,000 elements, one lookup performs only about 20 comparisons and indirections. The logarithm is the reason the number stays small. Each comparison eliminates a large share of the remaining candidates, so the count of steps rises only as the logarithm of the element count. In the source's example, $\log_2(1{,}000{,}000)$ is about 20, which matches the stated figure.

Elements in the map (n)Approximate steps for a map lookup, $\log_2(n)$
1,000about 10
1,000,000about 20
1,000,000,000about 30

Growth of a logarithmic lookup. The step counts are the base-2 logarithm of n, rounded, so each thousandfold increase in n adds only about 10 steps.

Worked example

Estimating the cost of a larger map

A map grows from 1,000,000 elements to 1,000,000,000 elements, a thousandfold increase. How many more comparisons per lookup does a logarithmic map lookup need?

  1. 1
    Identify the cost

    A map lookup costs $O(\log(n))$, so the step count is governed by the logarithm of n, not by n itself.

  2. 2
    Count the steps at the first size

    For n = 1,000,000, $\log_2(1{,}000{,}000)$ is about 20, matching the source's figure.

  3. 3
    Count the steps at the second size

    For n = 1,000,000,000, $\log_2(1{,}000{,}000{,}000)$ is about 30.

  4. 4
    Compare the change

    The collection grew a thousandfold, but the lookup needs only about 10 more steps.

Result

A thousandfold growth in the map adds only about 10 comparisons per lookup. The work grows far more slowly than the collection, which is why logarithmic lookup suits large maps.

Taking a Hashed Route Instead

The two containers differ in the route a lookup takes. Both routes start from a key and end at an element, but the middle step is different. A map's route passes through comparisons against an ordering, which is why its cost is logarithmic. A hashed lookup avoids comparison using an ordering function. The source states that in many cases we can do better by using a hashed lookup instead, so this is the central idea of the article: the map's cost comes from relying on ordering, and a hashed route takes a different path that can beat that cost in many situations.

Keysearch inputCompare usingorderingfor example less-thanMove left or rightrepeat as candidates shrinkElementfound by comparisonsKeysearch inputNo ordering functionnot requiredCandidate locationreached without acomparison walkElementfaster in many casesstepstepstepstepstepstep
How a hashed lookup jumps straight to a candidate location, while an ordered map lookup walks through comparisons, and where the two paths diverge

Notice what the diagram does not show. The source does not describe how a hashed container arranges its elements internally, so the comparison here focuses only on the lookup route. One container searches by comparing against an ordering, and the other avoids that ordering requirement entirely. Do not read the hashed route as a guarantee: the source calls the faster result a tendency, not a promise.

Why Unordered Does Not Mean Messy

The standard-library hashed containers are called unordered because they do not require an ordering function. The name describes a requirement, not a messy or random layout. A map needs an ordering function such as the less-than operator to decide how its elements relate and how to search them. An unordered_map does not need that ordering function at all, so the name points to the absence of that requirement rather than to some disorder that a programmer must cope with.

Mistakes Beginners Make with Lookup Containers

  • Common mistake

    Assuming a map lookup is slow because it is a comparison-based search

    Why it fails

    The source calls the $O(\log(n))$ cost pretty good, and a map of 1,000,000 elements needs only about 20 comparisons and indirections.

    Do this instead

    Judge the cost by its growth rate. A logarithmic lookup stays cheap even as the collection becomes large.

  • Common mistake

    Believing that unordered_map is always faster than map

    Why it fails

    The source says unordered_map provides faster lookup than a map in many cases, but this is a tendency and not a guarantee.

    Do this instead

    Treat the hashed container as a strong candidate for lookup, and confirm the choice against the actual workload when speed matters.

  • Common mistake

    Reading unordered as meaning the elements are stored randomly and cannot be handled

    Why it fails

    The name refers to the absence of a required ordering function, not to disorder in the container.

    Do this instead

    Remember that unordered describes a requirement the container does not impose, so it does not imply chaos.

  • Common mistake

    Choosing a hashed container because it sounds more advanced, without first stating what the code needs from the container

Deciding Between Ordered and Hashed Lookup

The source gives a clear decision rule. If the goal is efficient lookup, consider a hashed lookup as an alternative to comparison using an ordering function, since it can be better in many cases. A hashed container is a reasonable choice when your main operation is finding an element by its key. A map remains the right tool when the ordering itself matters to your program, because a map uses an ordering function to decide how its elements relate. The source's own description of unordered containers, that they do not require an ordering function, is the test: if your code needs no ordering, the unordered route is available to you.

Checking a Container Choice

MEDIUM

Your program stores product records keyed by product ID. Its most frequent operation is looking up one product by ID. Occasionally it must print all product IDs in sorted order. Which container would you consider for the lookups, and what does the sorted-printing requirement tell you about the choice?

Hints
  • Ask which operation dominates the workload: single-key lookup or ordered traversal.
  • Recall that an unordered_map does not require an ordering function, and the source does not describe any traversal order for it.
  • Consider whether a map's use of an ordering function is something your code needs.

Key Takeaways

  • A map lookup costs $O(\log(n))$, so the step count grows far more slowly than the number of elements; a map of 1,000,000 elements needs about 20 comparisons and indirections.
  • A map searches by comparison using an ordering function such as the less-than operator, while a hashed lookup avoids that comparison and can be faster in many cases, though not guaranteed.
  • The standard-library hashed containers are called unordered because they do not require an ordering function, not because their contents are disordered.
  • Choose unordered_map when the work is mainly fast lookup by key and the code does not depend on an ordering of the elements; choose a map when that ordering matters.

More concepts starting with C