Golang program to implement a hash table with separate chaining


In computer science, hash tables are a crucial data structure for fast data retrieval. It is also known as hashmap and it stores and retrieves data based on a key-value pair. In this article we will implement a hash table in go with independent chaining. In the examples demonstrated below we are going to perform the operations like initialization, insertion and then displaying the hash table.

Explanation

As a data structure each slot in the hash table has a linked list of items that hash to the same index, making separate chaining a collision resolution strategy. In this method, many items may share the same index, and collisions are avoided by the use of linked lists.

Here we have a hash table with corresponding array index for understanding:

Array Index:   0     1     2     3     4
Hash Table:  |     |     |     |     |     |
  • Now if we want to start at a value using a key, we need to pass the key through the hash function and it will produce an index to store value, let us assume the index is 2.

  • To retrieve the value, directly use the hash function to find index and access value at index.

Array Index:   0     1     2     3     4
Hash Table: |     |     |fruit|     |     |
                            ^ key
                            value: "apple"

	Retrieve("fruit"):
Array Index:   0     1     2     3     4
Hash Table: |     |     |fruit|     |     |
                            ^ key
                            value: "apple"

This is a simulation of a hash table operation with a simple hash function, initially we have a array based hash table with empty slots, then we add the key value pair “apple” into hash table using a hash function that determines its index based on key, To retrieve the value with key fruit, use the hash function to find index where the value is stored, and you will find the value.

Syntax

func Initialize(size int) *HashTable

The syntax defines a function named Initialize which allocates memory for the hash table, setting its size, and initializes the internal map used for separate chaining by accepting an integer size as input and returning a pointer to a HashTable structure.

func (ht *HashTable) HashCode(key int) int

The syntax defines a function named HashCode accepting an integer key as input and returning the index at which the key-value pair should be placed using a modulo operation of the size of the hash table.

Algorithm

  • First, we'll populate the hash table using a linked-list array.

  • To compute a hash code, provide a key.

  • For mapping the hash code to an index in the array use modulo operation.

  • Insert the element at the calculated index in the linked list.

  • During retrieval, calculate the hash code, find the linked list at the index, and search for the element.

Example 1

In this example,implement a hash table in Go, we build a hash table with separate chaining for collision resolution using a HashTable structure containing an array of linked list nodes, which are created to store key-value pairs. Initialize function initializes the hash table, HashCode method computes the index using modulo operation, while Insert function adds elements, handling collisions by chaining nodes. In the main function, a hash table is made, elements are inserted, and the resulting hash table is displayed, demonstrating the collision resolution mechanism using separate chaining.

package main
import "fmt"
type KeyValuePair struct{ Key, Value int }
type Node struct{ Data KeyValuePair; Next *Node }
type HashTable struct{ Size int; Table []*Node }
func Initialize(size int) *HashTable { return &HashTable{size, make([]*Node, size)} }
func (ht *HashTable) HashCode(key int) int { return key % ht.Size }
func (ht *HashTable) Insert(key, value int) {
	index := ht.HashCode(key)
	node := &Node{Data: KeyValuePair{key, value}}
    if ht.Table[index] == nil { ht.Table[index] = node } else { current := ht.Table[index]; for current.Next != nil { current = current.Next }; current.Next = node }
}
func DisplayHashTable(ht *HashTable) {
	for index, node := range ht.Table {
    	fmt.Printf("Index %d:", index)
        for current := node; current != nil; current = current.Next {
            fmt.Printf(" -> (%d, %d)", current.Data.Key, current.Data.Value)
    	}
        fmt.Println()
    }
}
func main() {
	hashTable := Initialize(10)
	hashTable.Insert(5, 42)
	hashTable.Insert(13, 78)
	hashTable.Insert(26, 91)
	hashTable.Insert(39, 104)
	hashTable.Insert(42, 117)
	fmt.Println("Hash Table after insertions:")
	DisplayHashTable(hashTable)
}

Output

Hash Table after insertions:
Index 0:
Index 1:
Index 2: -> (42, 117)
Index 3: -> (13, 78)
Index 4:
Index 5: -> (5, 42)
Index 6: -> (26, 91)
Index 7:
Index 8:
Index 9: -> (39, 104)

Example 2

In this example, implement a hash table in go, HashTable structure used to make the hash table with separate chaining consists of a map of integer keys to slices of integer values. Initialize function creates the hash table with a given size, HashCode method calculates an index using modulo operation, while Insert method inserts values into the hash table, addressing collisions by appending values to existing indices. In the main function, the hash table is made, values are inserted, and the final state of the hash table, considering separate chaining, is displayed.

package main
import (
	"fmt"
)
type HashTable struct {
  	Size  int
	Table map[int][]int
}
func Initialize(size int) *HashTable {
	table := make(map[int][]int)
	return &HashTable{Size: size, Table: table}
}
func (ht *HashTable) HashCode(key int) int {
	return key % ht.Size
}
func (ht *HashTable) Insert(key int, value int) {
	index := ht.HashCode(key)
	if ht.Table[index] == nil {
    	ht.Table[index] = []int{value}
	} else {
        ht.Table[index] = append(ht.Table[index], value)
	}
}
func DisplayHashTable(ht *HashTable) {
	for index, values := range ht.Table {
    	fmt.Printf("Index %d: %v\n", index, values)
    }
}
func main() {
	hashTable := Initialize(10)
    hashTable.Insert(5, 42)
	hashTable.Insert(13, 78)
	hashTable.Insert(26, 91)
	hashTable.Insert(39, 104)
	hashTable.Insert(42, 117)
	fmt.Println("Hash Table after insertions:")
	DisplayHashTable(hashTable)
}

Output

Hash Table after insertions:
Index 5: [42]
Index 3: [78]
Index 6: [91]
Index 9: [104]
Index 2: [117]

Real Life Implementation

  • Library Catalogs: These records are often handled by libraries using hash tables with independent chaining. Each book's unique identifier, such as the ISBN, may be assigned an index by the hash table. Libraries employ separate chaining to properly handle scenarios in which many volumes are allocated the same index during the hashing process. This method guarantees that the books are effectively arranged and retrieved.

  • User Sessions in Web Applications: User sessions in online applications are often managed using hash tables with separate chaining. To effectively obtain their own session data, each user's session ID may be subjected to a hashing method. Separate chaining ensures that several users' sessions are properly stored and retrieved, even if their IDs result in the same index during the hashing process.

Conclusion

Hash tables are essential data structures used for efficient data retrieval and storage by mapping keys to indices in an array, to allow quick access to values. Separate Chaining is one of the common techniques used to handle collisions in hash tables. In this article, we have seen 2 methods to implement a hash table in go. The first approach, using arrays of linked lists, organises elements within arrays, directly addressing indices for retrieval. The second approach, using maps, offers dynamic key-value pairing, adapting to varying collisions, potentially reducing memory usage while maintaining effective data access.

Updated on: 18-Oct-2023

116 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements