数组、切片与映射的基本使用与地址 | 青训营

83 阅读5分钟

数组、切片与映射的基本使用与地址

数组变量的类型包括数组的长度和每个元素的类型,只有这两个部分都相同的数组,才是类型相同的数组,才能相互赋值

切片是一种数据结构,有三个字段,分别是指向底层数组的指针、切片访问的元素的个数(长度)、切片允许增长到的元素的个数(容量)

🎈切片初始化

  1. make
slice:=make([]string,5)
slice:=make([]int,3,5)
//不允许创建容量小于长度的切片
slice:=make([]int,5,3)
// len larger than cap in make([]int)
  1. 切片字面量
slice:=[]string{"RED","BLUE"}
slice:=[]int{10,20,30}

⭐数组 / 切片的复制

数组/切片指针复制,两个指针指向同一地址

func main() {
	array1 := [3]*string{new(string), new(string), new(string)}
	*array1[0] = "Red"
	*array1[1] = "Blue"
	*array1[2] = "Green"
	array2 := array1
	fmt.Println("Array1 Address: ", array1)
	fmt.Println("Array2 Address: ", array2)
	fmt.Printf("Array1[0]: %s , Array2[0]: %s\n", *array1[0], *array2[0])
	*array2[0] = "Yellow"
	fmt.Println("Change Array2[0] = "Yellow"")
	fmt.Println("Array1 Address: ", array1)
	fmt.Println("Array2 Address: ", array2)
	fmt.Printf("Array1[0]: %s , Array2[0]: %s", *array1[0], *array2[0])
}
/* print:
	Array1 Address:  [0xc000014270 0xc000014280 0xc000014290]
	Array2 Address:  [0xc000014270 0xc000014280 0xc000014290]
	Array1[0]: Red , Array2[0]: Red
	==Change Array2 , Stay Array1==
	Array1 Address:  [0xc000014270 0xc000014280 0xc000014290]
	Array2 Address:  [0xc000014270 0xc000014280 0xc000014290]
	Array1[0]: Yellow , Array2[0]: Yellow
*/

数组值复制,分别存在不同地址

func main() {
	array1 := [3]string{"Red", "Blue", "Green"}
	array2 := array1
	fmt.Printf("Array1 Address: %p\n", &array1)
	fmt.Println("Array1 Value: ", array1)
	fmt.Printf("Array2 Address: %p\n", &array2)
	fmt.Println("Array2 Value: ", array2)
	array2[0] = "Yellow"
	fmt.Println("Change Array2[0] = "Yellow"")
	fmt.Printf("Array1 Address: %p\n", &array1)
	fmt.Println("Array1 Value: ", array1)
	fmt.Printf("Array2 Address: %p\n", &array2)
	fmt.Println("Array2 Value: ", array2)
}
/* print:
	Array1 Address: 0xc0000161b0
	Array1 Value:  [Red Blue Green]
	Array2 Address: 0xc0000161e0
	Array2 Value:  [Red Blue Green]
	Change Array2[0] = "Yellow"
	Array1 Address: 0xc0000161b0
	Array1 Value:  [Red Blue Green]
	Array2 Address: 0xc0000161e0
	Array2 Value:  [Yellow Blue Green]
*/

切片值复制,是对相同数组的不同引用,不同地址修改同一内容

func main() {
	array1 := []string{"Red", "Blue", "Green"}
	array2 := array1
	fmt.Printf("Array1 Address: %p\n", &array1)
	fmt.Println("Array1 Value: ", array1)
	fmt.Printf("Array2 Address: %p\n", &array2)
	fmt.Println("Array2 Value: ", array2)
	array2[0] = "Yellow"
	fmt.Println("Change Array2[0] = "Yellow"")
	fmt.Printf("Array1 Address: %p\n", &array1)
	fmt.Println("Array1 Value: ", array1)
	fmt.Printf("Array2 Address: %p\n", &array2)
	fmt.Println("Array2 Value: ", array2)
}
/* print:
	Array1 Address: 0xc0000b0018
	Array1 Value:  [Red Blue Green]
	Array2 Address: 0xc0000b0030
	Array2 Value:  [Red Blue Green]
	Change Array2[0] = "Yellow"
	Array1 Address: 0xc0000b0018
	Array1 Value:  [Yellow Blue Green]
	Array2 Address: 0xc0000b0030
	Array2 Value:  [Yellow Blue Green]
*/

三个索引创建切片

  1. 两个索引创建切片

对底层数组容量是k的切片slice[i:j]来说:

长度: j - i

容量: k - i

  1. 三个索引创建切片

对于slice[i:j:k][2:3:4]

长度:j - i 或 3 - 2 = 1

容量:k - i 或 4 - 2 = 2

映射Map

结构&实现

映射的散列表包含一组桶。在存储、删除或者查找键值对的时候,所有操作都要先选择一个桶。把操作映射时指定的键传给映射的散列函数,就能选中对应的桶。这个散列函数的目的是生成一个索引,这个索引最终将键值对分布到所有可用的桶里。

Go语言的映射生成散列键的过程比图4-25展示的过程要稍微长一些,不过大体过程是类似的。在我们的例子里,键是字符串,代表颜色。这些字符串会转换为一个数值(散列值)。这个数值落在映射已有桶的序号范围内表示一个可以用于存储的桶的序号。之后,这个数值就被用于选择桶,用于存储或者查找指定的键值对。对Go语言的映射来说,生成的散列键的一部分,具体来说是低位(LOB),被用来选择桶。

如果再仔细看看图4-24,就能看出桶的内部实现。映射使用两个数据结构来存储数据。第一个数据结构是一个数组,内部存储的是用于选择桶的散列键的高八位值。这个数组用于区分每个键值对要存在哪个桶里。第二个数据结构是一个字节数组,用于存储键值对。该字节数组先依次存储了这个桶里所有的键,之后依次存储了这个桶里所有的值。实现这种键值对的存储方式目的在于减少每个桶所需的内存。

创建 & 初始化

dict := make(map[string]int)

dict := map[string][string]{"Red":"#da1337","Orange":"#e95a22"}

映射的键可以是任何值。这个值的类型可以是内置的类型,也可以是结构类型,只要这个值可以适用==运算符进行比较。切片、函数以及包含切片你的结构类型这些类型由于具有引用语义,不可以作为映射的键,适用这些类型会造成编译错误

dict := map[[]string]int{}
// Compier Exception:
// incalid map key type []string

应用

测试映射里是否存在某个键是映射的一个中药操作

value,exists:= colordict["Blue"]
if exists{
    fmt.Println(value)
}

删除

delete(colordict,"Pink")

小结

  • 数组是构造切片和映射的基石
  • Go语言里切片经常用来处理数据的集合,映射用来处理具有键值对结构的数据
  • 内置函数make可以创建切片和映射,并指定原始的长度和容量。也可以直接适用切片和映射字面量,或者适用字面量作为变量的初始值
  • 切片有容量限制,不过可以使用内置的append函数扩展容量
  • 映射的增长没有容量或者任何限制
  • 内置函数len可以用来获取切片或者映射的长度
  • 内置函数cap只能用于切片
  • 通过组合,可以创建多维数组和多维切片。也可以适用切片或者其他映射作为映射的值,但是不能用切片作为映射的键。
  • 将切片或者映射传递给函数的成本很小,并且不会复制底层的数据结构