February 23, 2025
How Hash Code is Computed/ Working
Great question! Letβs clarify how the **hash code** is actually computed in Java and how it is used in a `HashMap`. The hash code is notβ¦
By Imrankhan
3 min read
Great question! Let's clarify how the hash code is actually computed in Java and how it is used in a
HashMap. The hash code is not something we assume β it is computed by thehashCode()method, which is defined in theObjectclass and can be overridden by specific classes (likeString,Integer, etc.).
β -
How Hash Code is Computed
- Default
hashCode()Method: β TheObjectclass provides a default implementation of thehashCode()method. β This default implementation typically converts the memory address of the object into an integer (not guaranteed, but commonly implemented this way). β Example:
Object obj = new Object(); int hash = obj.hashCode(); // Returns a hash code based on the object's memory address
> 2. **Overridden `hashCode()` Method**:
β Many classes in Java (like `String`, `Integer`, etc.) override the `hashCode()` method to provide a meaningful hash code based on the object's content.
β For example:
β **String**: The hash code of a `String` is computed based on its characters.
β **Integer**: The hash code of an `Integer` is simply its integer value.
> β -
> ### **How `HashMap` Uses the Hash Code**
> When you insert a key-value pair into a `HashMap`, the following steps occur:
> 1. **Compute the Hash Code**:
β The `hashCode()` method of the key is called to compute the hash code.
β Example:
String key = "Alice";
int hash = key.hashCode(); // Returns a hash code based on the characters in "Alice"
> 2. **Overridden `hashCode()` Method**:
β Many classes in Java (like `String`, `Integer`, etc.) override the `hashCode()` method to provide a meaningful hash code based on the object's content.
β For example:
β **String**: The hash code of a `String` is computed based on its characters.
β **Integer**: The hash code of an `Integer` is simply its integer value.
> β -
> ### **How `HashMap` Uses the Hash Code**
> When you insert a key-value pair into a `HashMap`, the following steps occur:
> 1. **Compute the Hash Code**:
β The `hashCode()` method of the key is called to compute the hash code.
β Example:
String key = "Alice";
int hash = key.hashCode(); // Returns a hash code based on the characters in "Alice"
- Adjust the Hash Code: β The
HashMapinternally applies a supplemental hash function to the hash code to further reduce collisions. This is done using the following formula:
hash = key.hashCode() ^ (key.hashCode() >>> 16)
β This step ensures that the higher bits of the hash code are also considered in the index calculation.
> 3. **Compute the Index**:
β The adjusted hash code is used to compute the index in the array using:
index = (n β 1) & hashβ This step ensures that the higher bits of the hash code are also considered in the index calculation.
> 3. **Compute the Index**:
β The adjusted hash code is used to compute the index in the array using:
index = (n β 1) & hashβ n is the capacity of the array (number of buckets).
β & is the bitwise AND operator.
β -
Example: Hash Code Calculation for a String
Let's take the key
"Alice"and compute its hash code step by step.
- Compute
hashCode()for"Alice": β TheStringclass overrides thehashCode()method to compute the hash code based on the characters in the string. β The formula forString.hashCode()is:
s[0]*31^(n-1) + s[1]*31^(n-2) + β¦ + s[n-1]s[0]*31^(n-1) + s[1]*31^(n-2) + β¦ + s[n-1]β s[i] is the i-th character of the string.
β n is the length of the string.
β ^ denotes exponentiation.
- For
"Alice":
A = 65, l = 108, i = 105, c = 99, e = 101
hashCode = 65 * 31β΄ + 108 * 31Β³ + 105 * 31Β² + 99 * 31ΒΉ + 101 * 31β°
hashCode = 65 * 923521 + 108 * 29791 + 105 * 961 + 99 * 31 + 101 * 1
hashCode = 60028265 + 3217428 + 100905 + 3069 + 101
hashCode = 63306768A = 65, l = 108, i = 105, c = 99, e = 101
hashCode = 65 * 31β΄ + 108 * 31Β³ + 105 * 31Β² + 99 * 31ΒΉ + 101 * 31β°
hashCode = 65 * 923521 + 108 * 29791 + 105 * 961 + 99 * 31 + 101 * 1
hashCode = 60028265 + 3217428 + 100905 + 3069 + 101
hashCode = 63306768
- Adjust the Hash Code: β Apply the supplemental hash function:
hash = 63306768 ^ (63306768 >>> 16)hash = 63306768 ^ (63306768 >>> 16)β 63306768 >>> 16 shifts the bits of 63306768 16 places to the right:
63306768 in binary: 00000011 11000101 10100010 10010000
After >>> 16: 00000000 00000000 00000011 1100010163306768 in binary: 00000011 11000101 10100010 10010000
After >>> 16: 00000000 00000000 00000011 11000101β Perform XOR (^) operation:
00000011 11000101 10100010 10010000
^ 00000000 00000000 00000011 11000101
β β β β β β β β β β β β β β β β β β
00000011 11000101 10100001 0101010100000011 11000101 10100010 10010000
^ 00000000 00000000 00000011 11000101
β β β β β β β β β β β β β β β β β β
00000011 11000101 10100001 01010101β The adjusted hash is 63306765.
- Compute the Index: β Assume the capacity (
n) is 16. β Compute the index:
index = (16β1) & 63306765
index = 15 & 63306765index = (16β1) & 63306765
index = 15 & 63306765β Binary representation of 15: 00001111.
β Binary representation of 63306765: 00000011 11000101 10100001 01010101.
β Perform bitwise AND (&):
00001111
& 00000011 11000101 10100001 01010101
β β β β β β β β β β β β β β β β β β
00000101 (5 in decimal)00001111
& 00000011 11000101 10100001 01010101
β β β β β β β β β β β β β β β β β β
00000101 (5 in decimal)β The index is 5.
β -
Dry Run with Real Hash Codes
Let's perform a dry run with the following key-value pairs:
("Alice", 25)("Bob", 30)("Charlie", 35)
Step 1: Initialize HashMap
- Capacity (
n) = 16. - Load factor = 0.75.
- Threshold =
16 * 0.75 = 12.
Step 2: Insert
("Alice", 25)
- Compute hash code for
"Alice": βhashCode("Alice") = 63306768.
- Adjust the hash code: β
hash = 63306768 ^ (63306768 >>> 16) = 63306765.
- Compute index: β
index = (16β1) & 63306765 = 5.
- Insert at index
5: β Bucket at index5is empty, so a new node is created:
Node(hash=63306765, key="Alice", value=25, next=null)Node(hash=63306765, key="Alice", value=25, next=null)Step 3: Insert
("Bob", 30)
- Compute hash code for
"Bob": βhashCode("Bob") = 2070756.
- Adjust the hash code: β
hash = 2070756 ^ (2070756 >>> 16) = 2070756.
- Compute index: β
index = (16β1) & 2070756 = 4.
- Insert at index
4: β Bucket at index4is empty, so a new node is created:
Node(hash=2070756, key="Bob", value=30, next=null)Node(hash=2070756, key="Bob", value=30, next=null)Step 4: Insert
("Charlie", 35)
- Compute hash code for
"Charlie": βhashCode("Charlie") = -204629542.
- Adjust the hash code: β
hash = -204629542 ^ (-204629542 >>> 16) = -204629542.
- Compute index: β
index = (16β1) & -204629542 = 10.
- Insert at index
10: β Bucket at index10is empty, so a new node is created:
Node(hash=-204629542, key="Charlie", value=35, next=null)Node(hash=-204629542, key="Charlie", value=35, next=null)β -
Final HashMap Structure
After the above operations, the
HashMaplooks like this:
Index 0β3: null Index 4: Node("Bob", 30) -> null Index 5: Node("Alice", 25) -> null Index 6β9: null Index 10: Node("Charlie", 35) -> null Index 11β15: null
> β -
> ### **Key Takeaways**
1. The `hashCode()` method is used to compute the hash code of the key.
2. The `HashMap` adjusts the hash code to reduce collisions.
3. The index is computed using `(n β 1) & hash`.
4. Collisions are handled using linked lists or trees.
> This step-by-step explanation and dry run should give you a clear understanding of how hash codes are computed and used in a `HashMap`!
> β -
> ### **Key Takeaways**
1. The `hashCode()` method is used to compute the hash code of the key.
2. The `HashMap` adjusts the hash code to reduce collisions.
3. The index is computed using `(n β 1) & hash`.
4. Collisions are handled using linked lists or trees.
> This step-by-step explanation and dry run should give you a clear understanding of how hash codes are computed and used in a `HashMap`!