天天看点

Go 语言切片前言 – 数组和切片声明过程的比较切片的声明及初始化切片的二个属性 – 长度和容量切片的地址和扩容切片截取切片拷贝和排序切片作为函数参数传递

Go 语言自学

  • 前言 -- 数组和切片声明过程的比较
  • 切片的声明及初始化
  • 切片的二个属性 -- 长度和容量
  • 切片的地址和扩容
  • 切片截取
  • 切片拷贝和排序
  • 切片作为函数参数传递

以下原则仅个人娱乐使用:

& 租房原理:一个人来租房经常会订二个人的份,称为预留

& 二倍原则和四分之一原则:当切片的长度超过容量时,容量将变成原来的二倍;当容量大于 1024 时,每次将增加原来容量的 1/4

& 容量不超父亲原则:截取切片的容量索引值不能超过原始切片的容量值:cap(原始切片名)

& 切片截取子影响父原则:截取切片的改变将间接导致原始切片发生改变

& 切片拷贝不变原则:拷贝后的切片的改变不会导致原始切片发生改变

& 传递切片地址变不变原则:调用函数传递切片的地址,在函数内部使用赋值语句将不会改变形参的地址;在函数内部使用 append 函数追加将改变形参的地址

前言 – 数组和切片声明过程的比较

先看一个一维数组初始化的例子

语法:var 数组名 [数组长度]数组类型

package main

import "fmt"

func main() {

	// 声明一个整型数组
	var arr_int [5]int
}
           

再看切片声明的例子

语法:var 切片名 []切片类型

package main

import "fmt"

func main() {

	// 声明一个整型空切片
	var slice_int []int
}
           

切片的声明及初始化

1. 空切片

重点:利用 append 函数往切片中添加值

package main

import "fmt"

func main() {

	// 声明一个空切片
	var slice_int []int

	// 利用 append 函数给切片中添加值
	arr_int = append(slice_int, 1, 2, 3, 4, 5)

	// 打印切片内的值
	fmt.Println(slice_int) // [1 2 3 4 5]
}
           

2. 非空切片和自动推导

package main

import "fmt"

func main() {

	// 初始化一个非空切片
	slice_int := []int{1, 2, 3}

	// 打印切片内的值
	fmt.Println(slice_int) // [1 2 3]
}
           

切片的二个属性 – 长度和容量

1. 不指定长度和容量来初始化切片

package main

import "fmt"

func main() {

	// 初始化一个非空切片
	slice_int := []int{1, 2, 3}

	// 打印长度和容量
	fmt.Println(len(slice_int), cap(slice_int))

	// output:3 3
}
           

2. 指定长度和容量来声明切片 – 及赋值操作

重点:在指定长度的空切片之后利用 append 进行追加数据将导致原本的长度和容量发生变化

package main

import "fmt"

func main() {

	// 声明切片
	slice_int := make([]int, 5, 10)

	// 打印长度和容量
	fmt.Println(len(slice_int), cap(slice_int)) // 5 10

	slice_int = append(slice_int, 1, 2, 3, 4, 5, 6)

	fmt.Println(slice_int) // [0 0 0 0 0 1 2 3 4 5 6]

	// 打印长度和容量
	fmt.Println(len(slice_int), cap(slice_int)) // 11 20
}
           

切片的赋值

重点:实质上利用了下标

package main

import "fmt"

func main() {

	// 声明切片
	slice_int := make([]int, 5, 10)

	// 追加数据
	slice_int = append(slice_int, 1, 2, 3, 4, 5, 6)

	fmt.Println(slice_int) // [0 0 0 0 0 1 2 3 4 5 6]

	// 赋值
	slice_int[0] = -3
	slice_int[1] = -2
	slice_int[2] = -1
	slice_int[3] = 0

	fmt.Println(slice_int) // [-3 -2 -1 0 0 1 2 3 4 5 6]
}
           

切片的地址和扩容

切片:空切片

如下程序证明:切片名本身就是一个地址;空切片是指向内存地址编号为 0 的空间;空切片的长度和容量均为 0(也就是说不能进行赋值操作)

package main

import "fmt"

func main() {

	// 声明空切片
	var slice_int []int

	// 打印空切片的地址
	fmt.Printf("%p\n", slice_int) // 0x0
	
	// 空切片的长度及容量
	fmt.Println(len(slice_int), cap(slice_int)) // 0 0
}
           

如下程序证明:空切片只能用 append 函数进行追加操作,而且追加后切片的地址、长度和容量都将发生变化;此处长度和容量的变化不统一(容量常比长度多 1,类似租房原理)

package main

import "fmt"

func main() {

	// 声明空切片
	var slice_int []int

	// append 函数追加数据
	slice_int = append(slice_int, 1, 2, 3, 4, 5)

	// 追加数据后切片的地址
	fmt.Printf("%p\n", slice_int) // 0xc00000a36012345

	// 追加数据后切片的长度和容量
	fmt.Println(len(slice_int), cap(slice_int)) // 5 6

	// 打印切片
	fmt.Println(slice_int) // [1 2 3 4 5]
}
           

切片:指定了长度和容量的切片

如下程序证明:使用 append 给指定了长度和容量的切片追加数据后,切片的地址、长度和容量均会发生改变;区别在于容量的改变规则不同 (遵循二倍原则和四分之一原则)

package main

import "fmt"

func main() {

	// 声明长度为 2 容量为 5 的切片
	slice_int := make([]int, 2, 5)

	// 1. 第一次 append 函数追加数据
	slice_int = append(slice_int, 1, 2)

	// 第一次追加数据后切片的地址
	fmt.Printf("%p\n", slice_int) // 0xc0000d6030

	// 第一次追加数据后切片的长度和容量
	fmt.Println(len(slice_int), cap(slice_int)) // 4 5

	// 第一次打印切片
	fmt.Println(slice_int) // [0 0 1 2]

	// 2. 第二次 append 函数追加数据
	slice_int = append(slice_int, 3, 4)

	// 第二次追加数据后切片的地址
	fmt.Printf("%p\n", slice_int) // 00120xc00000c1e0

	// 第二次追加数据后切片的长度和容量
	fmt.Println(len(slice_int), cap(slice_int)) // 6 10

	// 第二次打印切片
	fmt.Println(slice_int) // [0 0 1 2 3 4]
}
           

切片截取

语法:截取切片 := 原始切片[起始索引:终止索引:容量索引]

对如上语法的解释:截取切片的长度取决于终止索引和起始索引的差;截取切片的容量取决于容量索引和起始索引的差;比较重要的是,容量索引不能大于原始切片的容量值(即容量不超父亲原则)

package main

import "fmt"

func main() {

	// 初始化切片
	slice_int := []int{1, 2, 3, 4, 5}

	// 打印切片的长度和容量
	fmt.Println(len(slice_int), cap(slice_int)) // 5 5

	// 切片截取
	slice2_int := slice_int[1:4:5]

	// 打印截取后切片的长度和容量
	fmt.Println(len(slice2_int), cap(slice2_int)) // 3 3
}
           

重点:利用赋值操作改变截取后的切片将影响到原始切片(即切片截取子影响父原则)

package main

import "fmt"

func main() {

	// 初始化切片
	slice_int := []int{1, 2, 3, 4, 5}

	// 1. 切片截取
	slice2_int := slice_int[1:4:5]

	// 打印截取切片
	fmt.Println(slice2_int) // [2 3 4]

	// 打印原始切片
	fmt.Println(slice_int) // [1 2 3 4 5]

	// 2. 利用赋值操作修改截取切片
	slice2_int[0] = 200

	// 打印原始切片
	fmt.Println(slice_int) // [1 200 3 4 5]
}
           

切片拷贝和排序

切片拷贝

重点:拷贝切片不会改变原切片的长度及容量;切拷贝后的切片发生改变将不会导致原始切片发生改变(即切片拷贝不变原则)

package main

import "fmt"

func main() {

	// 初始化二个切片
	slice1_int := []int{1, 2}
	slice2_int := []int{3, 4, 5, 6, 7}

	// 打印二个切片的长度和容量
	fmt.Println(len(slice1_int), cap(slice1_int)) // 2 2
	fmt.Println(len(slice2_int), cap(slice2_int)) // 5 5

	// 1. copy 拷贝 -- 将切片 2 拷贝到切片 1 上
	copy(slice1_int, slice2_int)

	// 打印二个切片的长度和容量
	fmt.Println(len(slice1_int), cap(slice1_int)) // 2 2
	fmt.Println(len(slice2_int), cap(slice2_int)) // 5 5

	// 打印切片 1 和切片 2
	fmt.Println(slice1_int) // [3 4]
	fmt.Println(slice2_int) // [3 4 5 6 7]

	// 初始化二个切片
	slice3_int := []int{1, 2}
	slice4_int := []int{3, 4, 5, 6, 7}

	// 打印二个切片的长度和容量
	fmt.Println(len(slice3_int), cap(slice3_int)) // 2 2
	fmt.Println(len(slice4_int), cap(slice4_int)) // 5 5

	// 2. copy 拷贝 -- 将切片 3 拷贝到切片 4 上
	copy(slice4_int, slice3_int)

	// 打印二个切片的长度和容量
	fmt.Println(len(slice3_int), cap(slice3_int)) // 2 2
	fmt.Println(len(slice4_int), cap(slice4_int)) // 5 5

	// 打印切片 3 和切片 4
	fmt.Println(slice3_int) // [1 2]
	fmt.Println(slice4_int) // [1 2 5 6 7]

	for _, value := range slice4_int {
		fmt.Print(value, " ") // outputs:1 2 5 6 7
	}
}
           

切片排序

自定义场景:利用切片进行 int 类型数据的排序(冒泡排序)

package main

import "fmt"

func main() {

	// 定义 9 个数据的乱序切片
	slice_int := []int{1, 9, 2, 8, 3, 7, 4, 6, 5}

	// 输出排序前的切片
	fmt.Println(slice_int) // [1 9 2 8 3 7 4 6 5]

	// 进行排序
	for i := 0; i < len(slice_int)-1; i++ {
		for j := 0; j < len(slice_int)-1-i; j++ {
			if slice_int[j] > slice_int[j+1] {
				slice_int[j], slice_int[j+1] = slice_int[j+1], slice_int[j]
			}
		}
	}

	// 输出排序后的切片
	fmt.Println(slice_int) // [1 2 3 4 5 6 7 8 9]
}
           

切片作为函数参数传递

重点:函数中使用 append 进行数据追加,形参地址发生改变(即传递切片地址变不变原则)

package main

import "fmt"

func Change(slice_int []int) {
	// 赋值改变
	slice_int[0] = 100
}

func NoChange(slice_int []int) {
	// append 函数追加数据
	slice_int = append(slice_int, 4, 5, 6)
}

func main() {

	// 初始化切片
	slice1_int := []int{1, 2, 3}
	slice2_int := []int{1, 2, 3}

	// 第一次打印切片
	fmt.Println(slice1_int) // [1 2 3]
	fmt.Println(slice2_int) // [1 2 3]

	// 调用 NoChange 和 Change 函数
	NoChange(slice1_int)
	Change(slice2_int)

	// 第二次打印切片
	fmt.Println(slice1_int) // [1 2 3]
	fmt.Println(slice2_int) // [100 2 3]
}