Concepts / Associative Containers and Hash Functions

Associative Containers and Hash Functions

An associative container relies on a hash function to place elements, so the hash function's quality matters.

  • Programming

Why the Hash Function Decides Placement

An associative container that uses hashing has to decide where each element goes. It makes that decision with a hash function, which takes an element and produces a hash value. The container then uses that value to choose a place to store the element. When you use a container from the standard library, the container supplies its own machinery for storing and finding elements, but the function that maps your elements to hash values is still a decision you make. That decision is the subject of this article.

The source material gives three guiding ideas. The first is to test the hash function you choose rather than assume it works. The second is to combine standard hash functions with exclusive or when an element has several parts that all matter for its identity. The third is to prefer standard-library containers to structures you build by hand. Keep one question in mind as you read: given an element, what does its hash value look like, and how is that value produced? The sections below follow a value through a combined hash function and then through a test run, so you can see what each step contributes.

Tracing an Element to Its Bucket

Think of the container as a table of slots, which this article calls buckets. The hash function looks at an element and produces a hash value, and that value determines which bucket the element lands in. Two different elements can in principle end up in the same bucket. The diagram below traces three elements through this process so you can see where each one ends up. The hash values shown are illustrative numbers chosen for this explanation, not values taken from the source.

Element Ahash value 9bucket 0Element Bhash value 4bucket 1Element Chash value 13bucket 2bucket 3placed inplaced inplaced in
How does a hash function turn a key into a bucket index, and where does each element end up in the table?

Read the diagram from the elements downward. Element A and Element C both map to bucket 1, so they share a bucket, while Element B has its own bucket 0 and bucket 2 and bucket 3 remain free. The point is not the particular arithmetic that produced these buckets, which the source does not specify. The point is that the hash function alone decides the bucket, which means a hash function that spreads values unevenly will crowd elements into a few buckets and leave others almost empty. That is exactly why the quality of the hash function matters.

Combining Hash Values with Exclusive Or

Suppose an element has two parts that both matter for its identity. The source describes one approach: hash each part with a standard hash function, then combine the two results using exclusive or. Each standard hash function handles only one piece of the data, and the combining step is a small, well-defined operation. Nothing in this flow requires you to invent a new mixing scheme from scratch. The source says that hash functions built this way are often good, but it does not explain the operation in depth, so the rest of this section works through a small numeric case.

Part one of elementStandard hash of partoneExclusive orcombining stepCombined hash valuePart two of elementStandard hash of parttwohashedhashedfirst valuesecond valueproduces
How do the bits of two hash values flow through an exclusive or operation to produce a single combined hash?

Read the diagram from top to bottom. The two parts enter separately, each passes through its own standard hash function, and the two results meet at the exclusive or step, which yields one combined value. The combining step is where the two halves of the element's identity are merged into a single number that the container can use for placement.

Exclusive or compares two bit patterns position by position. For each bit position, the result bit is 1 when the two input bits differ and 0 when they match. Working through a case shows the effect directly. The two values used here are illustrative numbers chosen for this explanation, not values from the source.

Worked example

Combining two hash values with exclusive or

Two standard hash functions produce the bit patterns 101 and 011 for the two parts of one element. Combine them with exclusive or.

  1. 1
    Line up the bits

    Write the two patterns one above the other so that position one, two, and three line up: 1 0 1 above 0 1 1.

  2. 2
    Compare position one

    The first bits are 1 and 0. They differ, so the result bit is 1.

  3. 3
    Compare position two

    The second bits are 0 and 1. They differ, so the result bit is 1.

  4. 4
    Compare position three

    The third bits are 1 and 1. They match, so the result bit is 0.

  5. 5
    Assemble the result

    Reading the result bits in order gives 110, so $101 \oplus 011 = 110$.

Result

The combined hash value is 110. Both input parts influenced the result, which is the purpose of combining them.

What do you think happens?

Before reading the answer, predict the result of combining 101 and 011 with exclusive or. Which option do you choose?

  • 011

  • 110

  • 101

  • 000

Reveal answer
Answer:

110

Exclusive or gives a 1 in each position where the two bits differ. Positions one and two differ, and position three matches, so the result is 110.

Checking a Hash Function with an Experiment

The first guidance in the source is to experiment to check that you have an acceptable hash function. Writing a hash function is not enough. You need evidence that it behaves acceptably for the data you actually store. The flowchart below shows the stages of a simple test run, from choosing sample keys to deciding whether the candidate is acceptable.

Choose sample keysresembling real dataHash each keycandidate functionCount bucketoccupancykeys per bucketAcceptable spread?judged from observed dataKeep the functionRevise the functionthen test againfeed keys incollect valuescompareyesnorerun
What sequence of steps tests a hash function, and what is the decision point?
  1. 1
    Choose sample keys that resemble the data your program will really store, and include the parts of an element that the hash function must handle.
  2. 2
    Feed the sample keys into the candidate hash function and collect the hash values it produces.
  3. 3
    Count how many keys land in each bucket, so you can see whether the values spread out or pile up.
  4. 4
    Judge the observed spread. The source does not give exact acceptability criteria, so base this judgment on the behavior you see in your own test data.
  5. 5
    If the function is acceptable, keep it. If not, revise it and run the test again.

The decision point is the judgment at the diamond. Keeping a function is a conclusion drawn from evidence, and a function that fails the test goes back for revision rather than being shipped. When the function combines standard hash functions with exclusive or, the test must cover the combined result as well as its parts, because the combining step is part of the function you will actually use.

Mistakes When Choosing and Testing Hash Functions

  • Common mistake

    Assuming a hash function is good because it was written carefully

    What it looks like

    A function is written, looks reasonable, and is used without any test run.

    Why it fails

    The source says writing a hash function is not enough. You need evidence that it behaves acceptably for the data you store.

    Do this instead

    Run a test with representative keys and examine how the values spread across buckets before relying on the function.

  • Common mistake

    Testing with keys that do not resemble real data

    What it looks like

    The test uses a handful of simple, evenly spaced keys, while the program stores records with several varied parts.

    Why it fails

    A test is only as useful as its sample. Unlike keys produce results that may say little about real use.

    Do this instead

    Choose sample keys that look like the real data and include every part of an element the hash function must handle.

  • Common mistake

    Testing the parts but not the combined result

    What it looks like

    Each standard hash function is checked on its own, and the exclusive or combination is never tested.

    Why it fails

    The combining step is part of the function you actually use, so its behavior must be observed too.

    Do this instead

    Test the final combined hash value as well as the individual hashes.

  • Common mistake

    Treating an unacceptable result as a final verdict instead of a reason to revise the function and test again

Standard Containers Versus Hand-Built Structures

The third guidance in the source is to know your standard-library containers and prefer them to hand-crafted data structures. The idea is to start from what the standard library already provides instead of building a new structure whenever you need one. The source gives the reason only implicitly. A hand-crafted structure requires its author to get every detail right, including the hashing behavior discussed earlier. A standard-library container moves that responsibility onto a component that is already part of the language, so your effort goes into choosing it well and using it correctly.

ConcernStandard-library containerHand-crafted structure
Where the storage machinery comes fromSupplied by a component already part of the languageWritten by you from scratch
Who must get the hashing details rightThe container's component, with your choice of hash function still matteringYou, for every detail
What you still decideWhich container to use, and which hash function to give itEvery part of the design, including the hashing behavior
Where your effort goesChoosing and using the container wellBuilding and maintaining the whole structure

What the programmer has to do under each approach

Designing Your Own Test Run

MEDIUM

A program stores customer records, and each record has two parts that both identify it: a name and an account number. You plan to hash each part with a standard hash function and combine the results with exclusive or. Describe a simple experiment that would tell you whether this combined hash function is acceptable. Name the sample keys you would choose, what you would count, and what result would make you revise the function.

Hints
  • Start by choosing sample keys that look like real customer records, with both parts filled in.
  • Remember to test the combined value as well as each part's hash, since the combining step is part of the final function.
  • Count how many records fall into each bucket, and ask whether they spread out or pile up in a few buckets.
  • The source does not give exact numeric criteria, so state the observation that would lead you to keep or revise the function.

Key Takeaways

  • An associative container uses a hash function to decide where each element is stored, so the quality of that function directly shapes how elements are distributed.
  • When an element has several parts that matter for identity, the source suggests hashing each part with a standard hash function and combining the results with exclusive or, then testing the combined result.
  • Experimenting with representative sample keys is the source's recommended way to check whether a hash function is acceptable, and a failing result sends you back to revise the function.
  • Prefer standard-library containers to hand-crafted data structures, because their hashing machinery is already part of the language, while you still decide the hash function you give them.

Related concepts

More concepts starting with A