乐趣区

关于go: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)

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

bars = append(bars, barElement)

slice实际上是一个构造体类型,蕴含 3 个字段,别离是

  • array: 是指针,指向一个数组,切片的数据理论都存储在这个数组里。
  • len: 切片的长度。
  • cap: 切片的容量,示意切片以后最多能够存储多少个元素,如果超过了现有容量会主动扩容。

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

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

type Pointer *ArbitraryType

如果依照下面的示例先创立一个长度为 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
}

这行代码 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
}

这种办法也能够,也无需扩容。

那办法 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-…
退出移动版