go1.18beta1 泛型demo: hashmap

Posted yzbmz5913

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 := &entrykey: 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 := &entrykey: 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,十分别扭。

希望后续版本中能够改进。

以上是关于go1.18beta1 泛型demo: hashmap的主要内容,如果未能解决你的问题,请参考以下文章

Go1.18都出泛型了,不来试试看?

Go1.18都出泛型了,不来试试看?

Go1.18都出泛型了,不来试试看?

分享 2 个 Go1.18 新特性的官方教程

Go官方 - 1.18 发布说明

京东不再需要CTO;架构师成长路线;Go1.18泛型编程体验|码农周刊VIP会员专属邮件周报 Vol.085