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,十分别扭。
希望后续版本中能够改进。
标签:return,beta1,demo,fmt,value,next,key,泛型,table 来源: https://www.cnblogs.com/stan-marsh/p/15832324.html