码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • Go十大常见错误第6篇:slice初始化常犯的错误


    前言

    这是Go十大常见错误系列的第6篇:slice初始化常犯的错误。素材来源于Go布道者,现Docker公司资深工程师Teiva Harsanyi。

    本文涉及的源代码全部开源在:Go十大常见错误源代码,欢迎大家关注公众号,及时获取本系列最新更新。

    场景

    假设我们知道要创建的slice的长度,你会怎么创建和初始化这个slice?

    比如我们定义了一个结构体叫Bar,现在要创建一个slice,里面的元素就是Bar类型,而且该slice的长度是已知的。

    方法1

    有的人可能这么来做,先定义slice

    var bars []Bar
    bars := make([]Bar, 0)
    • 1

    每次要往bars这个slice插入元素的时候,通过append来操作

    bars = append(bars, barElement)
    • 1

    slice实际上是一个结构体类型,包含3个字段,分别是

    • array: 是指针,指向一个数组,切片的数据实际都存储在这个数组里。
    • len: 切片的长度。
    • cap: 切片的容量,表示切片当前最多可以存储多少个元素,如果超过了现有容量会自动扩容。

    slice底层的数据结构定义如下:

    type slice struct {
     array unsafe.Pointer
     len   int
     cap   int
    }

    type Pointer *ArbitraryType
    • 1

    如果按照上面的示例先创建一个长度为0的slice,那在append插入元素的过程中,bars这个slice会做自动扩容。

    如果bars的长度比较大,可能会发生多次扩容。每次扩容都要创建一个新的内存空间,然后把旧内存空间的数据拷贝过来,效率比较低。

    方法2

    在定义slice的时候指定长度,代码示例如下:

    func convert(foos []Foo) []Bar {
     bars := make([]Bar, len(foos))
     for i, foo := range foos {
      bars[i] = fooToBar(foo)
     }
     return bars
    }
    • 1

    这行代码 bars := make([]Bar, len(foos))直接指定了slice的长度,无需扩容。

    方法3

    在定义slice的时候提前指定容量,长度设置为0,代码示例如下:

    func convert(foos []Foo) []Bar {
     bars := make([]Bar, 0, len(foos))
     for _, foo := range foos {
      bars = append(bars, fooToBar(foo))
     }
     return bars
    }
    • 1

    这种方法也可以,也无需扩容。

    那方法2和方法3哪种好一点呢?其实各有优缺点:

    • 从效率上来说,方法2比方法3要高一点,因为方法3里调用了append函数,再对bars赋值,效率比直接通过 bars[i] = fooToBar(foo)要低一点。
    • 从代码的可维护性来说,方法2不能通过append函数来插入元素,因为方法2里的slice定义的时候指定了长度,如果调用append,会扩容,往现有元素后面追加元素。方法3不能通过 bars[i] = 的方式来赋值,因为方法3里的slice定义的时候长度为0,如果使用 bars[1]=,会触发 panic: runtime error: index out of range [1] with length 0。

    最后强烈推荐大家看看这道关于Go slice的面试题:Go Quiz: 从Go面试题看slice的底层原理和注意事项。

    看完后你会彻底了解Go slice的原理和注意事项。

    推荐阅读

    • Go十大常见错误第1篇:未知枚举值

    • Go十大常见错误第2篇:benchmark性能测试的坑

    • Go十大常见错误第3篇:go指针的性能问题和内存逃逸

    • Go十大常见错误第4篇:break操作的注意事项

    • Go十大常见错误第5篇:slice初始化常犯的错误

    • Go面试题系列,看看你会几题?

    开源地址

    文章和示例代码开源在GitHub: Go语言初级、中级和高级教程。

    公众号:coding进阶。关注公众号可以获取最新Go面试题和技术栈。

    个人网站:Jincheng's Blog。

    知乎:无忌。

    福利

    我为大家整理了一份后端开发学习资料礼包,包含编程语言入门到进阶知识(Go、C++、Python)、后端开发技术栈、面试题等。

    关注公众号「coding进阶」,发送消息 backend 领取资料礼包,这份资料会不定期更新,加入我觉得有价值的资料。还可以发送消息「进群」,和同行一起交流学习,答疑解惑。

    References

    • https://itnext.io/the-top-10-most-common-mistakes-ive-seen-in-go-projects-4b79d4f6cd65

    本文由 mdnice 多平台发布

  • 相关阅读:
    【技术美术图形部分】图形渲染管线3.0-光栅化和像素处理阶段
    win10中安装ros
    LQ0003 乘积尾零【数论】
    猿创征文|Cglib代理之代理类方法的动态传递
    如何调试 fastlane 源码
    K8S排水错误汇总(忽略DaemonSet管理Pod、Mysql集群排水报错、Mongo集群排水报错)
    计算机毕业设计选题推荐-基于数据可视化的智慧社区内网平台-Python项目实战
    识不足则多虑,威不足则多怒,信不足则多言
    Cinema 4D 2024 v2024.0.2
    MySQL——多表查询
  • 原文地址:https://blog.csdn.net/perfumekristy/article/details/126414989
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号