Basically, the thing you are trying to achieve with a hash function is to give all bits of the hash code a roughly 50% chance to leave or give away a specific item that needs to be hashed. Thus, no matter how many buckets your hash table has (or in another way, how many of the lower bits you take to determine the bucket number) - if each bit is as random as possible, then the element will always be assigned purely random bucket.
Now, in real life, many people use hash functions that are not so good. They have some randomness in some bits, but not in all of them. For example, imagine if you have a hash function whose bits 6-7 are offset - say, in a typical hash code of an object, they have a 75% chance of being set. In the above example, if our hash table has 256 buckets (i.e., the number of buckets comes from bits 0-7 of the hash code), then we discard the randomness that exists in bits 8-31, and the smaller part of the buckets will have a tendency to fill (i.e., those whose numbers are bits 6 and 7).
An additional hash function basically tries to extend any randomness in the hash codes to a larger number of bits. Thus, in our hypothetical example, the idea would be that some randomness from bits 8-31 would mix with the least significant bits and blur the shift of bits 6-7. It will still not be perfect, but better than before.
Neil coffey
source share