包含引用类型字段的自定义结构体,能作为map的key吗

这篇具有很好参考价值的文章主要介绍了包含引用类型字段的自定义结构体,能作为map的key吗。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 引言

在 Go 语言中,map是一种内置的数据类型,它提供了一种高效的方式来存储和检索数据。map是一种无序的键值对集合,其中每个键与一个值相关联。使用 map 数据结构可以快速地根据键找到对应的值,而无需遍历整个集合。

在 Go 语言中,map 是一种内置的数据类型,可以通过以下方式声明和初始化:

m := make(map[keyType]valueType)

在使用map时,我们通常会使用基本数据类型作为键。然而,当我们需要将自定义的结构体作为键时,就需要考虑结构体中是否包含引用类型的字段。引用类型是指存储了数据的地址的类型,如指针、切片、字典和通道等。在Go中,引用类型具有动态的特性,可能会被修改或指向新的数据。这就引发了一个问题:能否将包含引用类型的自定义结构体作为map的键呢?

2. map的基本模型

了解能否将包含引用类型的自定义结构体作为map的键这个问题,我们需要先了解下map的基本模型。在Go语言中,map是使用哈希表、实现的。哈希表是一种以键-值对形式存储数据的数据结构,它通过使用哈希函数将键映射到哈希值。

哈希函数是用于将键映射到哈希值的算法。它接受键作为输入并生成一个固定长度的哈希值。Go语言的 map 使用了内部的哈希函数来计算键的哈希值。

而不同的key通过哈希函数生成的哈希值可能是相同的,此时便发生了哈希冲突。哈希冲突指的是不同的键经过哈希函数计算后得到相同的哈希值。由于哈希函数的输出空间远远小于键的输入空间,哈希冲突是不可避免的。此时无法判断该key是当前哈希表中原本便已经存在的元素还是由于哈希冲突导致不同的键映射到同一个bucket。 此时便需要判断这两个key是否相等。

因此,在map中,作为map中的key,需要保证其支持对比操作的,能够比较两个key是否相等。

3. map 键的要求

从上面map基本的模型介绍中,我们了解到,map中的Key需要支持哈希函数的计算,同时键的类型必须支持对比操作。

map中,计算key的哈希值,是由默认哈希函数实现的,对于map中的key并没有额外的要求。

map中,判断两个键是否相等是通过调用键类型的相等运算符(==!=)来完成的,因此key必须确保该类型支持 == 操作。这个要求是由 map 的实现机制决定的。map 内部使用键的相等性来确定键的存储位置和检索值。如果键的类型不可比较,就无法进行相等性比较,从而导致无法准确地定位键和检索值。

在 Go 中,基本数据类型(如整数、浮点数、字符串)和一些内置类型都是可比较的,因此它们可以直接用作 map 的键。然而,自定义的结构体作为键时,需要确保结构体的所有字段都是可比较的类型。如果结构体包含引用类型的字段,那么该结构体就不能直接用作 map 的键,因为引用类型不具备简单的相等性比较。

因此,假如map中的键为自定义类型,同时包含引用字段,此时将无法作为map的键,会直接编译失败,代码示例如下:

type Person struct {
   Name    string
   Age     int
   address []Address
}
func main() {
    // 这里会直接编译不通过
    m := make(map[Person]int)
}

其次还有一个例外,那便是自定义结构体中包含指针类型的字段,此时其是支持==操作的,但是其是使用指针地址来进行hash计算以及相等性比较的,有可能我们理解是同一个key,事实上从map来看并不是,此时非常容易导致错误,示例如下:

type Person struct {
   Name    string
   Age     int
   address *Address
}
func main(){
    m := make(map[Person]int)
    p1 := Person{Name: "Alice", Age: 30, address: &Address{city: "beijing"}}
    p2 := Person{Name: "Alice", Age: 30, address: &Address{city: "beijing"}}
    m[p1] = 1
    m[p2] = 2
    // 输出1
    fmt.Println(m[p1])
    // 输出2
    fmt.Println(m[p2])
}

这里我们定义了一个Person结构体,包含一个指针类型的字段address。创建了两个对象p1p2,在我们的理解中,其是同一个对象,事实上在map中为两个两个互不相关的对象,主要原因都是使用地址来进行hash计算以及相等性比较的。

综上所述,如果自定义结构体中包含引用类型的字段(指针为特殊的引用类型),此时将不能作为map类型的key

4. 为什么不抽取hashCode和equals方法接口,由用户自行实现呢?

当前gomap中哈希值的计算,其提供了默认的哈希函数,不需要由用户去实现;其次key的相等性比较,是通过== 操作符来实现的,也不由用户自定义比较函数。那我们就有一个疑问了,为什么不抽取hashCode和equals方法接口,由用户来实现呢?

4.1 简单性和性能角度

相等性比较在 Go 语言中使用 == 操作符来实现,而哈希函数是由运行时库提供的默认实现。这种设计选择我理解可能基于以下几个原因:

  1. 简单性:对于默认哈希函数函数来说,其内置在语言中的,无需用户额外的实现和配置。这简化了 map 的使用。对于相等性比较操作,== 操作符进行比较是一种直观且简单的方式。在语法上,== 操作符用于比较两个值是否相等,这种语法的简洁性使得代码更易读和理解。
  2. 性能:默认的哈希函数是经过优化和测试的,能够在大多数情况下提供良好的性能。其次使用==来实现相等性比较,由于 == 操作符是语言层面的原生操作,编译器可以对其进行优化,从而提高代码的执行效率。

4.2 key不可变的限制

map键的不可变性也是一个考虑因素。基于==来判断对象是否相等,间接保证了键的不可变性。目前,==已经支持了大部分类型的比较,只有自定义结构体中的引用类型字段无法直接使用==进行比较。如果键中不存在引用类型字段,这意味着放入Map键的值在运行时不能发生变化,从而保证了键在运行时的不可变性。

如果key没有不可变的限制,那么之前存储在 map 中的键值对可能会出现问题。因为在放置元素时,map 会根据键的当前值计算哈希值,并使用哈希值来查找对应的存储位置。如果放在map中的键的值发生了变化,此时计算出来的hash值可能也发生变化,这意味数据放在了错误的位置。后续即使使用跟map中的键的同一个值去查找数据,也可能查找不到数据。

下面展示一个简单的代码,来说明可变类型作为key会导致的问题:

type Person struct {
    Name       string
    Age        int
    SliceField []string
}

func main() {
    person := Person{Name: "Alice", Age: 25, SliceField: []string{"A", "B"}}
    // 假设Person可以作为键,事实上是不支持的
    personMap := make(map[Person]string)
    personMap[person] = "Value 1"

    // 修改person中SliceField的值
    person.SliceField[0] = "X"

    // 尝试通过相同的person查找值
    fmt.Println(personMap[person]) // 输出空字符串,找不到对应的值
}

如果抽取equals方法接口,由用户自行实现,此时key的不可变性就需要用户实现,其次go语言也需要增加一些检测机制,这首先增加了用户使用的负担,这并不符合go语言设计的哲学。

4.3 总结

综上所述,基于简单性、性能和语义一致性的考虑以及键的不可变性,Go语言选择使用==操作符进行键的比较,而将哈希函数作为运行时库的默认实现,更加符合go语言设计的哲学。

5. 总结

在 Go 语言中,map 是一种无序的键值对集合,它提供了高效的数据存储和检索机制。在使用 map 时,通常使用基本数据类型作为键。然而,当我们想要使用自定义结构体作为键时,需要考虑结构体中是否包含引用类型的字段。

自定义结构体作为map的键需要满足一些要求。首先,键的类型必须是可比较的,也就是支持通过== 运算符进行相等性比较。在Go中,基本数据类型和一些内置类型都满足这个要求。但是,如果结构体中包含引用类型的字段,那么该结构体就不能直接作为map的键,因为引用类型不具备简单的相等性比较。

因此总的来说,包含引用类型字段的自定义结构体,是不能作为mapkey的。文章来源地址https://www.toymoban.com/news/detail-470721.html

到了这里,关于包含引用类型字段的自定义结构体,能作为map的key吗的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Elasticsearch教程12】Mapping字段类型之object

    首先给出结论: 在一个字段存储 一个 JSON对象,可以选择 object 类型 在一个字段存储 多个 JSON对象,可以选择 nested 类型 假设有一个人员信息如下: 建表时会建一个字段存一个值,它的缺点是字段 扁平化 , 无法直观的表现 层级关系 。 字段 值 region US manager_age 30 manager_firstn

    2024年02月09日
    浏览(39)
  • 【Elasticsearch学习笔记二】es的Mapping字段映射、Mapping字段常用类型、Mapping映射的创建、查看和更新、es数据迁移、ik分词器

    目录 1、Mapping字段映射概述 2、Mapping字段常用类型 3、映射中对时间类型详解 1)采取自动映射器来映射 2)手工映射提前指定日期类型 4、ES的keyword的属性ignore_above 5、Mapping映射的查看和创建 1)查看mapping信息:GET 索引名/_mapping 2)创建映射:PUT /索引名 3) 查看所有索引映

    2024年01月20日
    浏览(61)
  • es查询nested字段类型数组长度来判断是否包含多个值

    使用es时经常会碰到查询数组格式的字段是否有多个值的情况,总结一下。 一、字段类型为keywork时 当字段类型为keyword时(创建索引后,添加的第一条数据id字段格式为数组格式,便会导致后续的数据都是数组),查询id字段有多值时用以下查询语句: 二、字段类型为nested类型时

    2024年02月11日
    浏览(66)
  • 【Elasticsearch教程6】Mapping字段类型之二进制binary

    binary类型接收一个Base64编码的字符串,默认情况二进制字段 不能被存储和检索 。 binary不能被存储? 这个意思不是说没有存储在ES,而是说mapping中该字段的store参数默认是false。 默认情况下字段的值都会存储到 _source 里, binary 类型的值也是如此。 如果 store 属性设置为true,

    2024年02月05日
    浏览(56)
  • ES索引修改mappings与重建reindex详解之修改字段类型

    elasticsearch一直在使用,这里总结一下mappings的修改方法,分为两种情况: 增加新的字段,这种很简单; 修改已有的字段类型,这种就比较麻烦了,需要reindex,对索引进行迁移重建。 1.1、获取mappings 增加一个 new_stocks 字段,如下: 再查一下: 可以看到new_stocks字段已经加上去

    2024年02月17日
    浏览(44)
  • 【Elasticsearch教程11】Mapping字段类型之日期时间date date_nanos

    JSON没有 date 类型,但我们可以把以下类型作为日期时间存入ES。 类型 说明 字符串 日期格式的字符串,如\\\"2015-01-01\\\"或\\\"2015/01/01 12:10:30\\\" 长整型 从开始纪元(1970-01-01 00:00:00 UTC)开始的 毫秒数 整型 从开始纪元(1970-01-01 00:00:00 UTC)开始的 秒数 上面的UTC(Universal Time Coordinated) 叫

    2024年01月24日
    浏览(51)
  • mybatisPlus返回Map类型的集合(两列字段,一列为key,一列为value)

    1、自定义实现该类 2、在抽象dao层书写返回map集合类型的方法 3、在XXXDao.xml文件中书写sql语句和resultMap类型 4、如何使用

    2024年02月13日
    浏览(75)
  • 【算法与数据结构】3 知行合一,线性查找的自定义类测试

    欢迎来到爱书不爱输的程序猿的博客, 本博客致力于知识分享,与更多的人进行学习交流 本文收录于算法与数据结构体系专栏, 本专栏 对于0基础者极为友好,欢迎与我一起完成算法与数据结构的从0到1的跨越 ☑️首篇详细讲述线性查找法并且对其进行了 初步的优化 :👉传送门

    2023年04月27日
    浏览(48)
  • 使用Python爬取GooglePlay并从复杂的自定义数据结构中实现解析

    【作者主页】: 吴秋霖 【作者介绍】:Python领域优质创作者、阿里云博客专家、华为云享专家。长期致力于Python与爬虫领域研究与开发工作! 【作者推荐】:对JS逆向感兴趣的朋友可以关注《爬虫JS逆向实战》,对分布式爬虫平台感兴趣的朋友可以关注《分布式爬虫平台搭建

    2024年02月04日
    浏览(47)
  • go 结构体 - 值类型、引用类型 - 结构体转json类型 - 指针类型的种类 - 结构体方法 - 继承 - 多态(interface接口) - 练习

    目录 一、结构体 1、python 与 go面向对象的实现: 2、初用GO中的结构体:(实例化一个值类型的数据(结构体)) 输出结果不同的三种方式  3、实例化一个引用类型的数据(结构体) 4、引用类型(指针类型) vs 值类型(两者的区别) 引用类型(指针类型) - 值类型内存拓扑图:

    2024年02月14日
    浏览(54)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包