Go语言实现HashMap

223 阅读2分钟

在Go语言中,HashMap被定义为一种无序容器(即不按顺序存储元素的数据结构),其中存储的元素是以 key-value 形式保存的,使用 key 来进行访问。可以通过哈希算法将 key 映射为一个索引,然后存储在桶(bucket)中,以便在后续查找时进行快速访问。

定义

type node struct {
    key   string      // 键
    value interface{} // 值
    next  *node       // 链表指针
}

type hashMap struct {
    buckets []*node // 桶
    size    int     // 元素个数
}

New

// 创建一个新的HashMap
// 参数:
//     无
// 返回值:
//     *hashMap: 新创建的HashMap
func New() *hashMap {
    return &hashMap{
        buckets: make([]*node, 16),
        size:    0,
    }
}

常用方法

Set

// 存储一个键值对到HashMap
// 参数:
//     key:   键
//     value: 值
// 返回值:
//     无
func (m *hashMap) Set(key string, value interface{}) {
    index := hash(key) // 计算桶索引
    if m.buckets[index] == nil {
        m.buckets[index] = &node{key: key, value: value}
        m.size++
    } else {
        // 遍历链表,查找 key 是否存在
        current := m.buckets[index]
        for current != nil {
            if current.key == key {
                current.value = value // 如果 key 存在,更新 value
                return
            }
            current = current.next
        }
        // 如果 key 不存在,则在链表末尾添加一个新的节点
        newNode := node{key: key, value: value}
        newNode.next = m.buckets[index] // 原链表作为新节点的 next
        m.buckets[index] = &newNode    // 新节点作为头节点
        m.size++
    }
}

Get

// 从HashMap中获取一个值,根据键查找
// 参数:
//     key: 键
// 返回值:
//     interface{}: 查找到的值
//     bool:        是否存在该键
func (m *hashMap) Get(key string) (interface{}, bool) {
    index := hash(key) // 计算桶索引
    current := m.buckets[index]
    for current != nil {
        if current.key == key {
            return current.value, true // 返回找到的值和 true
        }
        current = current.next
    }
    return nil, false // 没找到,返回 nil 和 false
}

Remove

// 从HashMap中移除一个键值对
// 参数:
//     key: 键
// 返回值:
//     无
func (m *hashMap) Remove(key string) {
    index := hash(key) // 计算桶索引
    current := m.buckets[index]
    var prev *node
    for current != nil {
        if current.key == key {
            if prev != nil {
                prev.next = current.next // 修改前一个节点的指针
            } else {
                m.buckets[index] = current.next // 首节点被删除,修改 bucket 头指针
            }
            m.size--
            return
        }
        prev = current
        current = current.next
    }
}

hash

// 这个方法是实现一个哈希函数,将字符串映射为一个0到15的整数,可以用来将字符串存储在散列表中,便于快速查找。具体原理如下:
//
//首先将哈希值h初始化为0,然后遍历字符串的每一个字符(使用for循环)。
//对于每一个字符,将哈希值左移5位(h<<5),相当于将h乘以2的5次方,然后将哈希值右移27位(h>>27),相当于将h除以2的27次方,这样可以保证哈希函数分布均匀。然后将结果与字符的ASCII码相加,得到新的哈希值。
//最后我们希望将哈希值变为0到15的整数,所以使用模运算(%16)将哈希值映射到0到15的范围内。
//综合以上三个步骤,得到的就是字符串的哈希值,能够实现高效的散列表存储、查找操作。
func hash(key string) uint32 {
    h := 0
    for i := 0; i < len(key); i++ {
       h = (h << 5) | (h >> 27)
       h += int(key[i])
    }
    return h % 16
}