在Go语言中,map 是一种用于存储键值对的数据结构,也被称为字典或哈希表。它是一种无序的集合类型,用于在键(key)和值(value)之间建立映射关系。map 在Go语言中非常常用,适用于需要快速查找和检索数据的情况。
一些基本和使用方法
- 创建map: 可以使用make函数来创建一个空的map,并指定键和值的数据类型。
myMap := make(map[string]int) // 创建一个键为字符串,值为整数的空map
- 插入和获取数据: 可以使用键来插入和获取数据。例如:
myMap["key1"] = 100
value := myMap["key1"] // 获取键为"key1"的值
- 删除数据: 使用delete函数来删除map中的一个键值对。例如:
delete(myMap, "key1") // 删除键为"key1"的键值对,如果key1不存在myMap中,也是不报错的
- 检查键是否存在: 使用以下方式可以检查键是否存在于map中:
value, ok := myMap["key1"]
if ok {
// 键存在,进行处理
} else {
// 键不存在
}
- 迭代map: 使用range关键字可以迭代map中的所有键值对。迭代的顺序是不确定的,因为map是无序的。例如:
for key, value := range myMap {
fmt.Println(key, value)
}
- 注意事项:
- map 是引用类型,传递给函数时会传递引用,因此对map的修改会影响原始map。
- map 的键必须是可比较的类型(例如:整数、浮点数、字符串、指针、通道、接口和数组)。
- map 的值可以是任意类型。
- map 是无序的,无法保证迭代的顺序与插入的顺序一致
- 使用map需要注意并发访问的情况,如果多个协程同时访问和修改同一个map,需要采取适当的同步措施,例如使用互斥锁来保证数据的一致性。
- 那么,map为什么是无序的呢?
哈希表中的数据项并不是按照插入顺序进行存储的,而是根据键的哈希值计算出的索引位置来存储。这意味着,在map中迭代元素的顺序是不确定的,因为它们实际上不是按照某种特定顺序进行存储的。这样有以下的优点:
1:快速查找: map 的主要目的是提供一种快速的键值查找机制。为了实现这一点,map 内部使用了哈希表(hash table)或类似的数据结构。这使得按键查找元素的时间复杂度是平均 O(1),几乎是实时的。
2:内存管理: 无序的设计可以让map更好地适应动态内存管理。当元素被添加或删除时,内部数据结构可以更灵活地重新组织,而不需要保持稳定的顺序。
3:性能: 无序map的内部实现通常比有序map更加高效。有序的数据结构需要维护元素的顺序,这可能会导致插入和删除元素时的额外开销。
另外需要注意的是,由于哈希表的内部实现和优化策略可能会随着Go语言版本的不同而有所改变,所以在不同版本的Go中,map的迭代顺序可能也会发生变化。
练习一下吧
题目:实现一个缓存系统,有如下功能:
- 可以设置缓存的过期时间,如redis的过期时间
- 可以设置最大的内存大小,
- 可以获取当前的key数量以及清空内存。
- 定时清除已经过期的缓存数据
定义缓存的接口
type Cache interface {
SetMaxMemory(size string) bool
Set(key string, val interface{}, expire time.Duration) bool
Get(key string) (interface{}, bool)
Del(key string) bool
Flush() bool
Keys() int64
}
定义结构体
func NewMemCache() Cache {
mc := &memCache{
values: make(map[string]*memCacheValue, 0),
clearExpireItemTimeInterval: time.Second * 10,
}
go mc.clearExpireItem()
return mc
}
type memCache struct {
maxMemorySize int64
maxMemorySizeStr string //最大内存字符串表示
currMemorySize int64
values map[string]*memCacheValue
//锁:map非线程安全 需要加锁
locker sync.RWMutex //读写锁
clearExpireItemTimeInterval time.Duration
}
type memCacheValue struct {
val interface{}
expireTime time.Time
expire time.Duration
size int64
}
具体实现
// SetMaxMemory 设置最大内存值
func (mc *memCache) SetMaxMemory(size string) bool {
mc.maxMemorySize, mc.maxMemorySizeStr = ParseSize(size)
return false
}
// Set 设置值进缓存,需要带过期时间
func (mc *memCache) Set(key string, val interface{}, expire time.Duration) bool {
mc.locker.Lock()
defer mc.locker.Unlock()
v := &memCacheValue{
val: val,
expireTime: time.Now().Add(expire),
expire: expire,
size: GetValSize(val),
}
if (mc.maxMemorySize - mc.currMemorySize) < v.size {
log.Printf(fmt.Sprintf("max memory size %v \n", mc.maxMemorySize))
return false
}
mc.del(key)
mc.add(key, v)
return false
}
// Get 从缓存中获取值,如果过期了则立马删掉
func (mc *memCache) Get(key string) (interface{}, bool) {
mc.locker.Lock()
defer mc.locker.Unlock()
mcv, ok := mc.get(key)
if ok {
if mcv.expire != 0 && mcv.expireTime.Before(time.Now()) {
mc.del(key)
return nil, false
}
}
return mcv, true
}
// Del 删除缓存中的数据
func (mc *memCache) Del(key string) bool {
mc.locker.Lock()
defer mc.locker.Unlock()
mc.del(key)
return false
}
// Exist 判断key是否存在缓存中
func (mc *memCache) Exist(key string) bool {
mc.locker.Lock()
defer mc.locker.Unlock()
_, ok := mc.values[key]
return ok
}
func (mc *memCache) get(key string) (*memCacheValue, bool) {
val, ok := mc.values[key]
return val, ok
}
func (mc *memCache) del(key string) {
tmp, ok := mc.get(key)
if ok && tmp != nil {
mc.currMemorySize -= tmp.size
delete(mc.values, key)
}
}
func (mc *memCache) add(key string, val *memCacheValue) {
mc.values[key] = val
mc.currMemorySize += val.size
}
func (mc *memCache) Flush() bool {
mc.locker.Lock()
defer mc.locker.Unlock()
mc.values = make(map[string]*memCacheValue, 0)
mc.currMemorySize = 0
return false
}
func (mc *memCache) Keys() int64 {
mc.locker.Lock()
defer mc.locker.Unlock()
return int64(len(mc.values))
}
// 定期的去检查当前过期的key,过期了移除掉
func (mc *memCache) clearExpireItem() {
timeTicker := time.NewTicker(mc.clearExpireItemTimeInterval)
defer timeTicker.Stop()
for {
select {
case <-timeTicker.C:
for key, item := range mc.values {
if item.expire != 0 && time.Now().After(item.expireTime) {
mc.locker.Lock()
mc.del(key)
mc.locker.Unlock()
}
}
}
}
}
使用方式
cache := pkg.NewMemCache()
cache.SetMaxMemory("200MB")
cache.Set("int", 1)
cache.Get("int")
cache.Del("int")
cache.Flush()
cache.Keys()