一、切片 Slice
Go 语言数组的长度是不可变的,也就无法数组中添加元素,Go 提供了另一种长度可变的数组,既切片(动态数组),切片可以进行追加元素,相比数组来说更加灵活。
切片的定义
第一种定义方式
Go 切片的定义方式与数组非常类似,但是数组定义时需要指定数组长度,而切片定义时则无须指定长度。
func main() { // 定义切片 var langs = []string{"Go", "Python", "Ruby", "Java"} framworks := []string{"Rails", "Gin", "Play"} fmt.Printf("%T, %T", langs, framworks) } 复制代码
执行上述代码,输出结果如下:
[]string, []string 复制代码
与数组类型不同的是切片类型并不包含长度属性。
第二种定义方式
使用 make
函数也可以定义切片,make 函数还可以定义 map 以及 channel 数据类型并返回这些数据类型的实例。
func main() { // 定义切片 lans := make([]string, 3) fmt.Printf("%T, %v, %v", lans, lans, len(lans)) lans[0] = "Elixir" fmt.Printf("%T, %v, %v", lans, lans, len(lans)) } 复制代码
执行上述代码,输出结果如下:
[]string, [ ], 3 []string, [Elixir ], 3 复制代码
make 函数传入两个值,一个是类型,另一个是长度。
使用 make 函数定义切片时,切片中的元素为元素类型的默认值,切片是动态数组,也可以通过索引对元素进行修改。
第三种定义方式
可以通过 切片操作
来获取子数组,子数组的类型就是一个切片类型,这里要注意 切片操作
和 切片类型
的区分。
func main() { // 通过数组获取切片 lans := [3]string{"Elixir", "Scala", "Groovy"} // 获取子数组 jvm_lans := lans[1:] fmt.Printf("%T, %v, %v", jvm_lans, jvm_lans, len(jvm_lans)) } 复制代码
执行上述代码,输出结果如下:
[]string, [Scala Groovy], 2 复制代码
第四种定义方式
func main() { // 使用 new 函数定义切片 webFrameworks := *new([]string) fmt.Printf("%T, %v, %v\n", webFrameworks, webFrameworks, len(webFrameworks)) webFrameworks = append(webFrameworks, "Grails") fmt.Printf("%T, %v, %v", webFrameworks, webFrameworks, len(webFrameworks)) } 复制代码
执行上述代码,输出结果如下:
[]string, [], 0 []string, [Grails], 1 复制代码
new
函数调用时会返回一个内存地址,而 *内存地址
可以获取内存地址指向的实例,append 函数可以对切片进行追加操作,根据输出结果可以确定,初始化时切片的长度为 1,追加一个元素之后切片的长度变为 2。
make 函数和 new 函数都可以定义切片,它们的区别如下:
- make 和 new 都是用来分配内存的內建函数,且在堆上分配内存,make 即分配内存,也初始化内存;new只是将内存清零,并没有初始化内存。
- make 返回的还是引用类型(实例)本身;而 new 返回的是指向类型的指针(内存地址)。
- make 只能用来分配及初始化类型为 slice,map,channel;new 可以分配任意类型的数据。
切片是引用类型
数组是值类型,而切片是引用类型,同样可以通过在自定义函数中修改传入的切片类型的数据来验证切片是引用类型。
func main() { lans := []string{"Elixir", "Scala", "Ruby"} SliceHandler(lans) fmt.Println(lans) } func SliceHandler(sli []string) { fmt.Println(sli) for idx, item := range sli { sli[idx] = strings.ToUpper(item) } fmt.Println(sli) } 复制代码
执行上述代码,输出结果如下:
[Elixir Scala Ruby] [ELIXIR SCALA RUBY] [ELIXIR SCALA RUBY] 复制代码
原切片中的元素被修改,说明切片是引用类型。
二、切片的操作
追加
切片的追加操作可以通过内置的 append
函数实现,该函数需要传入要追加的切片以及要追加的元素,可以追加一个元素,也可以追加多个元素。
func main() { lans := []string{"Elixir", "Scala", "Ruby"} fmt.Printf("%T, %v, %v\n", lans, lans, len(lans)) lans = append(lans, "Groovy") fmt.Printf("%T, %v, %v\n", lans, lans, len(lans)) lans = append(lans, "Python", "Go", "Java") fmt.Printf("%T, %v, %v\n", lans, lans, len(lans)) } 复制代码
执行上述代码,输出结果如下:
[]string, [Elixir Scala Ruby], 3 []string, [Elixir Scala Ruby Groovy], 4 []string, [Elixir Scala Ruby Groovy Python Go Java], 7