数组

数组的概念

Go 中的数组是一种特定类型且长度固定的数据结构。 它们可具有零个或多个元素,你必须在声明或初始化它们时定义大小。 此外,它们一旦创建,就无法调整大小。 鉴于这些原因,数组在 Go 程序中并不常用,但它们是切片和映射的基础。

// 声明数组
var testArray1 [4]int
fmt.Println(testArray1)

// 初始化数组
var testArray2 = [3]int{}
fmt.Println(testArray2)

// 数组中的省略号
// 当给定了数组的值后,数组的长度可以用省略号,这样会自动计算长度
var testArray3 = [...]int{1, 2, 3}
fmt.Println(testArray3)

// 多维数组
var testArray4 = [2][3]int{{1, 2, 3}, {4, 5, 6}}
fmt.Println(testArray4)

此数组非彼数组

在 Go 里面的数组与 Python 里面的列表并不同(实际上与其他语言里面的数组概念也不同),Python 里面的列表更像是 Go 里面的切片,是一种容量可以随时变化的数据集,而 Go 里面的数组是一个固定大小的集,而且大小是不能变动的。所以这也决定了数组并不常用,它只是切片的底层。

切片 Slice

创建切片的几种方式

在 Go 中,创建切片的方式有多种,以下是一些常见的方式:

1. 字面量创建:

使用切片字面量直接创建切片。

// 创建一个包含元素的切片
slice1 := []int{1, 2, 3, 4, 5}

// 创建一个空切片
slice2 := []string{}

2. 使用 make 函数创建:

使用内建的 make 函数创建指定长度和容量的切片。

// 创建长度为 3,容量为 5 的整数切片
slice := make([]int, 3, 5)

3. 从数组中切割:

从现有数组中切割出一个切片。

array := [5]int{1, 2, 3, 4, 5}
slice := array[1:4] // 从索引 1 开始,到索引 4 结束(不包含索引 4)

4. 使用 append 函数追加元素:

使用 append 函数来动态添加元素。

// 创建一个空切片
slice := []int{}

// 使用 append 追加元素
slice = append(slice, 1, 2, 3)

5. 使用 copy 函数复制切片:

使用 copy 函数创建一个切片的副本。

// 创建一个切片
source := []int{1, 2, 3, 4, 5}

// 创建一个与源切片相同大小的切片
copySlice := make([]int, len(source))

// 使用 copy 复制切片
copy(copySlice, source)

切片的动态扩容机制

Go 的切片跟其他语言的数组或者列表一样,长度可以动态变动,也就是自动扩容,每次扩容的大小就是把原来的容量翻倍。

每次扩容会有性能的消耗,因此每次扩容就代表底层依赖的数组变动了,要创建新的数组并复制原来的数组。

oldSlice := make([]int, 4, 4)
fmt.Println(len(oldSlice), cap(oldSlice)) // 4 4

// 超过容量会自动扩容,每次扩容都是容量翻倍
oldSlice = append(oldSlice, 1)
fmt.Println(len(oldSlice), cap(oldSlice)) // 5 8

切片底层的数组

切片引用一个底层数组,切片的修改会影响底层数组的相应元素:

// 切片的修改会修改底层的数组
arr := [4]int{}
sli := arr[:]
fmt.Println(sli, arr) // [0 0 0 0] [0 0 0 0]
sli[0] = 1
fmt.Println(sli, arr) // [1 0 0 0] [1 0 0 0]

字符串和切片

string 底层就是一个 byte 的数组也是一个 rune 数组,因此,也可以进行切片操作。

// 纯英文字符串
en := "xyz"
enSli := []byte(en)
fmt.Println(enSli) // [120 121 122]

// 有中文的字符串
zh := "中文"
zhSli := []rune(zh)
fmt.Println(zhSli) // [20013 25991]

// 简单的翻转字符串,就是翻转切片的元素,然后转成字符串
zhSli[0], zhSli[1] = zhSli[1], zhSli[0]
fmt.Println(string(zhSli)) // 文中

string 本身是不可变的,因此要改变 string 中字符可以通过转化成切片然后修改之后再拼接回来,这个方式跟 Python 差不多。

一个例子,将一个纯数字的切片转成字符串:

var str string
sli := []int{1, 2, 3, 4}
var newSli []string
for _, value := range sli {
// 将数字转成字符串
newSli = append(newSli, strconv.Itoa(value))
}
// 将字符串的切片拼接成字符串
str = strings.Join(newSli, "")
fmt.Println(str) // 1234

切片知识点(ChatGPT 总结)

在 Go 中,切片是一种重要且灵活的数据结构,以下是掌握切片时需要注意的关键知识点:

  1. 切片的基本概念:

    • 了解切片是一种对数组的抽象,它提供了一种动态长度的、基于数组的数据结构。
  2. 切片的创建:

    • 掌握切片的创建方式,包括字面量创建、make 函数创建、从数组切割、使用 append 函数等。
  3. 切片的底层数组:

    • 切片引用一个底层数组,了解切片的修改会影响底层数组的相应元素。
  4. 切片的长度和容量:

    • 理解切片的长度(len())和容量(cap())的概念,以及它们之间的关系。
  5. 切片的动态扩容:

    • 了解切片在追加元素时可能触发的动态扩容机制,以及如何有效地使用 append 函数。
  6. 切片的零值:

    • 切片的零值是 nil,与 nil 比较可以判断一个切片是否被初始化。
  7. 切片与数组的区别:

    • 理解切片和数组的差异,特别是在长度、可变性和内存管理方面的不同。
  8. 使用 copy 函数:

    • 了解如何使用 copy 函数复制切片,并了解切片的拷贝操作是值拷贝还是引用拷贝。
  9. 切片作为函数参数:

    • 了解切片作为函数参数时是按值传递的,但实际上是传递了底层数组的引用。
  10. 多维切片:

    • 了解多维切片的概念和使用方法,以及如何初始化和操作多维切片。
  11. 使用切片的初始化表达式:

    • 掌握使用切片的初始化表达式来创建切片。
  12. 切片的安全使用:

    • 注意切片越界访问的问题,确保在操作切片时不会导致程序崩溃或产生未定义的行为。
  13. 空切片 vs nil 切片:

    • 了解空切片和 nil 切片的区别,以及在不同场景下应该使用哪一种。
  14. 切片和并发安全性:

    • 在并发环境下使用切片时要注意并发安全性,可以使用互斥锁等机制来保护切片的访问。

理解和掌握这些切片的关键知识点将使你能够更有效地使用切片,并避免一些常见的问题。

知识点总结

  1. 数组属于一种相对底层的数据结构,在一般使用中并不常用。
  2. 切片可以自动扩容,每次扩容的容量翻倍,但是有性能消耗,所以尽量初始化一个接近实际的容量,以减少自动扩容的频率。

版权声明:如无特殊说明,文章均为本站原创,转载请注明出处

本文链接:https://tendcode.com/subject/article/golang-study-4/

许可协议:署名-非商业性使用 4.0 国际许可协议