go1.18beta1 泛型demo: hashmap
go终于出泛型了,整个烂活玩玩,体验一下,总体感觉还是有点别扭,主要有两处不完善
// hashmap.go
package hashmap
import (
"bytes"
"encoding/gob"
"fmt"
"hash/fnv"
"reflect"
)
type HashMap[K comparable, V any] struct {
table []*entry
sz int
}
type entry struct {
key any
value any
next *entry
}
func NewHashMap[K comparable, V any](cap int) *HashMap[K, V] {
cap = powOfTwo(cap)
table := make([]*entry, cap, cap)
return &HashMap[K, V]{table: table}
}
const MaxCap = 1 << 30
func powOfTwo(cap int) int {
n := cap - 1
n |= n >> 1
n |= n >> 2
n |= n >> 4
n |= n >> 8
n |= n >> 16
if n < 0 {
return 1
}
if n >= MaxCap {
return MaxCap
}
return n + 1
}
func (m *HashMap[K, V]) Put(key K, value V) {
factor := float32(m.sz) / float32(len(m.table))
if factor > 0.75 {
m.resize()
}
h := m.hash(key)
idx := h & (uint64(len(m.table) - 1))
e := m.table[idx]
for e != nil {
if reflect.DeepEqual(e.key, key) {
e.value = value
return
}
e = e.next
}
n := &entry{key: key, value: value}
n.next = m.table[idx]
m.table[idx] = n
m.sz++
}
func (m *HashMap[K, V]) Get(key K) (v V, ok bool) {
h := m.hash(key)
idx := h & (uint64(len(m.table) - 1))
e := m.table[idx]
for e != nil {
if reflect.DeepEqual(e.key, key) {
return e.value.(V), true
}
e = e.next
}
return
}
func (m *HashMap[K, V]) Remove(key K) (v V, ok bool) {
defer func() {
if ok {
m.sz--
}
}()
h := m.hash(key)
idx := h & (uint64(len(m.table) - 1))
e := m.table[idx]
if reflect.DeepEqual(e.key, key) {
m.table[idx] = m.table[idx].next
return e.value.(V), true
}
for e.next != nil {
if reflect.DeepEqual(e.next.key, key) {
v = e.next.value.(V)
e.next = e.next.next
return v, true
}
e = e.next
}
return
}
func (m *HashMap[K, V]) Size() int {
return m.sz
}
func (m *HashMap[K, V]) Foreach(f func(key K, value V)) {
for _, e := range m.table {
if e == nil {
continue
}
for e != nil {
f(e.key.(K), e.value.(V))
e = e.next
}
}
}
func (m *HashMap[K, V]) resize() {
newC := uint64(len(m.table)) << 1
newTable := make([]*entry, newC, newC)
m.Foreach(func(key K, value V) {
h := m.hash(key)
idx := h & (newC - 1)
n := &entry{key: key, value: value}
n.next = newTable[idx]
newTable[idx] = n
})
m.table = newTable
}
func (m *HashMap[K, V]) hash(obj K) uint64 {
b := bytes.Buffer{}
_ = gob.NewEncoder(&b).Encode(obj)
h := fnv.New64a()
_, _ = h.Write(b.Bytes())
return h.Sum64()
}
func (m *HashMap[K, V]) String() string {
buf := bytes.NewBufferString("[")
i := 0
m.Foreach(func(key K, value V) {
buf.WriteString(fmt.Sprintf("<%v,%v>", key, value))
if i != m.sz-1 {
buf.WriteByte(',')
}
i++
})
buf.WriteRune(']')
return buf.String()
}
测试代码:
// main.go
package main
import (
"./hashmap"
"fmt"
)
func main() {
m := hashmap.NewHashMap[string, int](3)
m.Put("stan", 1)
m.Put("kyle", 2)
m.Put("kenny", 3)
m.Put("eric", 4)
m.Put("butters", 5)
m.Put("craig", 6)
m.Put("clyde", 7)
m.Put("jimmy", 8)
m.Put("token", 9)
m.Remove("eric")
m.Remove("butters")
fmt.Println(m.Get("stan"))
fmt.Println(m.Get("kyle"))
fmt.Println(m.Get("kenny"))
fmt.Println(m.Get("eric"))
fmt.Println(m.Get("butters"))
fmt.Println(m.Get("craig"))
fmt.Println(m.Get("clyde"))
fmt.Println(m.Get("jimmy"))
fmt.Println(m.Get("token"))
fmt.Println("-----------------------------")
fmt.Println(m)
}
基本使用确实没什么问题,但是有两处地方有点别扭
1. hashmap.go第56行、73行等处必须使用reflect.DeepEqual(e.key, key)而不是直接e.key == key。
难道==的语义不该看运行时类型吗?目前的处理方式貌似是因为是泛型参数,一律以iface看待,==也就会比较Value,显然不相等(因为Value.ptr不相等)
2. 不支持嵌套类型(也就是所谓内部类),导致entry的定义只能是
type entry struct {
key any
value any
next *entry
}
而不是
type entry[K comparable, V any] struct {
key K
value V
next *entry[K, V]
}
因为后者的泛型参数K和HashMap的泛型参数K用的是两个命名空间,不能互相赋值。。。所以只能用any类型占位,赋值时再强转成HashMap的泛型类型K、V,十分别扭。
希望后续版本中能够改进。
浙公网安备 33010602011771号