Golang 中哪些类型可以作为 map 类型的 key?

目录

[可以作为 map 键的类型](#可以作为 map 键的类型)

[不能作为 map 键的类型](#不能作为 map 键的类型)

最佳实践

小结


在 Go 语言中,map 是一种内置的关联数据结构类型,由一组无序的键值对组成,每个键都是唯一的,并与一个对应的值相关联。本文将详细介绍哪些类型的变量可以作为 map 的键,并通过实例进行说明。

可以作为 map 键的类型

因为 map 需要能够判断两个键是否相等以确保每个键的唯一性,所以并非所有类型都可以作为 map 的键,可以作为 map 键的数据类型必须满足以下条件:

  • 可比较性(Comparable):用于定义 map 键的类型必须是可比较的,也就是说,Go 语言能够确定两个相同类型的键是否相等。这要求该类型支持 == 操作符来进行比较。
  • 不可变性(Immutable):虽然 Go 语言规范并未明确指出键必须不可变,但由于 map的内部实现机制,键在创建后不能改变,因此通常选择不可变类型作为键。

以下是可以作为 map 键的类型:

  • 基本类型,几乎所有的基本类型(整型、浮点型、字符串、布尔型等)都可以作为 map 的键,因为它们都支持相等性比较。

    package main

    import "fmt"

    func main() {
    // 整数作为键
    mapInt := map[int]string{
    1: "one",
    2: "two",
    3: "three",
    }

    复制代码
      // 字符串作为键
      mapString := map[string]int{
      	"Alice": 25,
      	"Bob":   30,
      	"Eve":   22,
      }
    
      // 浮点数作为键(不推荐,因为浮点数的比较可能会因精度问题导致不准确)
      mapFloat64 := map[float64]string{
      	1.1: "one point one",
      	2.2: "two point two",
      	3.3: "three point three",
      }
    
      // 布尔值作为键
      mapBool := map[bool]string{
      	true:  "true",
      	false: "false",
      }
      fmt.Println(mapInt, mapString, mapFloat64, mapBool)

    }

  • 指针类型,指针类型可以作为 map 的键,因为指针的比较是基于指向的内存地址的。简单示例代码如下:

    package main

    import "fmt"

    func main() {
    type Person struct {
    Name string
    Age int
    }

    复制代码
      alice := &Person{"Alice", 25}
      bob := &Person{"Bob", 30}
    
      mapPointer := map[*Person]string{
         alice: "Alice's pointer",
         bob:   "Bob's pointer",
      }
      fmt.Println(mapPointer)

    }

  • 接口类型,接口类型可以作为 map 的键,只要接口的动态类型(即实际存储的类型)是可比较的。简单示例代码如下:

    package main

    import "fmt"

    type Equalizer interface {
    Equal(Equalizer) bool
    }

    type IntEqualizer int

    func (i IntEqualizer) Equal(e Equalizer) bool {
    other, ok := e.(IntEqualizer)
    return ok && i == other
    }

    func main() {
    mapInterface := map[Equalizer]string{
    IntEqualizer(1): "one",
    IntEqualizer(2): "two",
    IntEqualizer(3): "three",
    }
    fmt.Println(mapInterface)
    }

  • 结构体类型,结构体类型可以作为 map 的键,只要其所有字段都是可比较的。简单示例代码如下:

    package main

    import "fmt"

    func main() {
    type Point struct {
    X, Y int
    }

    复制代码
      mapStruct := map[Point]string{
         {1, 2}: "Point at (1,2)",
         {3, 4}: "Point at (3,4)",
      }
      fmt.Println(mapStruct)

    }

  • 数组类型,数组类型可以作为 map 的键,只要数组中的元素类型是可比较的。简单示例代码如下:

    package main

    import "fmt"

    func main() {
    arr1 := [3]int{1, 2, 3}
    arr2 := [3]int{4, 5, 6}
    mapArray := map[[3]int]string{
    arr1: "123",
    arr2: "456",
    }
    fmt.Println(mapArray)
    }

不能作为 map 键的类型

以下类型不能作为 map 的键:

  • 切片类型,因为切片是引用类型,其内容可能会变化,使得比较操作不确定。
  • 函数类型,因为 Go 语言中没有为函数定义相等性比较操作。
  • map 类型,map 类型不能作为 map 的键,因为也是引用类型,且没有定义相等性比较操作。
  • 包含上述不可比较类型的复合类型,任何包含上述不可比较类型(如切片、函数、映射)的复合类型,如结构体,也不能作为 map 的键。

最佳实践

  • 使用不可变类型作为键,map 的键必须是可比较的类型,可以使用任何内置的可比较类型,如 int、string、float 等。
  • 如果键是自定义类型,需要使该类型必须支持 == 和 != 比较运算。
  • 如果使用结构体作为键,需要保证结构体的字段不会被修改。如果结构体的字段发生变化,可能会导致无法找到键值对。
  • 虽然指针可以作为键,但是两个相同内容的不同指针会被视为不同的键。
  • 字符串作为键时,尤其是在有大量独特字符串时,可能会导致内存使用的增加。这种情况下,可以考虑使用字符串的哈希值作为键,但要注意潜在的散列冲突。
  • 最小化键的大小,更小的键可以减少内存的占用,同时可以提高查找的效率。

小结

在 Go 语言中,只有那些不可变并且可比较的类型才能作为 map 的键。在日常编程中,应该选择合适的键类型以确保 map 的高效和准确性。

相关推荐
程序员侠客行几秒前
Mybatis连接池实现及池化模式
java·后端·架构·mybatis
devmoon2 分钟前
运行时(Runtime)是什么?为什么 Polkadot 的 Runtime 可以被“像搭积木一样”定制
开发语言·区块链·智能合约·polkadot·runtmie
时艰.3 分钟前
Java 并发编程 — 并发容器 + CPU 缓存 + Disruptor
java·开发语言·缓存
Honmaple5 分钟前
QMD (Quarto Markdown) 搭建与使用指南
后端
忆~遂愿17 分钟前
GE 引擎进阶:依赖图的原子性管理与异构算子协作调度
java·开发语言·人工智能
沐知全栈开发21 分钟前
API 类别 - 交互
开发语言
PP东24 分钟前
Flowable学习(二)——Flowable概念学习
java·后端·学习·flowable
invicinble34 分钟前
springboot的核心实现机制原理
java·spring boot·后端
人道领域42 分钟前
SSM框架从入门到入土(AOP面向切面编程)
java·开发语言
铅笔侠_小龙虾42 分钟前
Flutter 实战: 计算器
开发语言·javascript·flutter