Golang 数组与切片
数组
- 介绍:
数组是同一类型元素的集合。 Go 语言中不允许混合不同类型的数据;
数组的个数也是数组类型的一部分,换句话来说,数组中的元素的数量是固定的,等同于python中的元组 注意: 如果是 interface{} 类型数组,可以包含任意类型
- 声明数组:
- 数组的表现形式: [n]T ;
- n: 表示数组中元素的数量;
- T:表示每个元素的类型;
- 声明数组后,若不初始化,则会将所有的元素全部赋值为该类型的零值;
- 示例:
package main import ( "fmt" ) func main() { var a [3]int //int array with length 3 fmt.Println(a) } // 打印内容:[0 0 0]
- 数组赋值:
- 根据索引赋值:
- 示例:
package main import ( "fmt" ) func main() { var a [3]int //int array with length 3 a[0] = 12 // array index starts at 0 a[1] = 78 a[2] = 50 fmt.Println(a) }
// 打印内容: [12 78 50]
- 简略声明,且初始化赋值:
- 在简略声明中,不需要将全部的元素都进行赋值,Go 会自动将多余的元素赋值为零值;
- 示例:
package main import ( "fmt" ) func main() { a := [3]int{12, 78, 50} // short hand declaration to create array fmt.Println(a) } // 打印内容:[12 78 50]
- 忽略数组长度,声明数组:
- 语法: [...]T{a,b,c}
- Go 的编译器会自动计算数组的长度;
- 示例:
package main import ( "fmt" ) func main() { a := [...]int{12, 78, 50} // ... makes the compiler determine the length fmt.Println(a) } // 打印内容:[12 78 50]
- 数组的类型问题:
- 数组是值类型;
- 当数组赋值给一个新的变量时,该变量会得到一个原始数组的一个副本。对新变量进行更改,不会影响原始数组;
- 示例:
1 // 示例1: 2 3 package main 4 5 import "fmt" 6 7 func main() { 8 a := [...]string{"USA", "China", "India", "Germany", "France"} 9 b := a // a copy of a is assigned to b 10 b[0] = "Singapore" 11 fmt.Println("a is ", a) 12 fmt.Println("b is ", b) 13 } 14 // 打印内容: 15 a is [USA China India Germany France] 16 b is [Singapore China India Germany France] 17 18 19 // 示例2: 20 package main 21 22 import "fmt" 23 24 func changeLocal(num [5]int) { 25 num[0] = 55 26 fmt.Println("inside function ", num) 27 } 28 func main() { 29 num := [...]int{5, 6, 7, 8, 8} 30 fmt.Println("before passing to function ", num) 31 changeLocal(num) //num is passed by value 32 fmt.Println("after passing to function ", num) 33 } 34 35 // 打印内容: 36 before passing to function [5 6 7 8 8] 37 inside function [55 6 7 8 8] 38 after passing to function [5 6 7 8 8]
- 数组的长度也是决定类型的一部分,不同长度的数组不能相互赋值:
- 数组不能调整大小,等同于python中的元组;
package main func main() { a := [3]int{5, 78, 8} var b [5]int b = a // not possible since [3]int and [5]int are distinct types } // 报错信息:cannot use a (type [3]int) as type [5]int in assignment。
- 函数len() ; 将数组传进去,即可获取数组的长度:
- 示例:
package main import "fmt" func main() { a := [...]float64{67.7, 89.8, 21, 78} fmt.Println("length of a is",len(a)) } // 打印内容:length of a is 4
range循环
- for 循环 range 方式。 它将返回索引和该索引处的值:
- 示例:
package main import "fmt" func main() { a := [...]float64{67.7, 89.8, 21, 78} sum := float64(0) for i, v := range a {//range returns both the index and value fmt.Printf("%d the element of a is %.2f\n", i, v) sum += v } fmt.Println("\nsum of all elements of a",sum) } // %.2f: 格式化代替浮点数 // 打印内容: 0 the element of a is 67.70 1 the element of a is 89.80 2 the element of a is 21.00 3 the element of a is 78.00 sum of all elements of a 256.5
切片
- 介绍:
切片是由数组建立的一种方便、灵活且功能强大的包装(Wrapper)。
切片本身不拥有任何数据。它们只是对现有数组的引用。
切片的长度是切片中的元素数。切片的容量是从创建切片索引开始的底层数组中元素数;
某种角度上来讲,这里的切片类同于python中的列表;
- 创建一个切片:
- 语法: []T
- 该语法和数组创建的类似,不过,中括号中不需要加点点点,就是切片;
- 切片对应的值: 切片对应的值,应当是已有的数组的值;
- 语法:a[start:end]
- 以上两种语法的示例:
1 // 示例1 2 package main 3 4 import ( 5 "fmt" 6 ) 7 8 func main() { 9 a := [5]int{76, 77, 78, 79, 80} 10 var b []int = a[1:4] // creates a slice from a[1] to a[3] 11 fmt.Println(b) 12 } 13 14 // 打印内容:[77 78 79] 15 16 17 // 示例2: 18 package main 19 20 import ( 21 "fmt" 22 ) 23 24 func main() { 25 c := []int{6, 7, 8} // 创建一个有 3 个整型元素的数组,并返回一个存储在 c 中的切片引用 26 fmt.Println(c) 27 } 28 29 // 打印内容:
- 使用make 创建切片:
- 语法: make([]T,len,cap);
- []T: 类型;
- len:长度;
- cap:容量;可选传参,默认值为切片长度
- make 函数创建一个数组,并返回引用该数组的切片。
- 示例:
package main import ( "fmt" ) func main() { i := make([]int, 5, 5) fmt.Println(i) } // 打印内容:[0 0 0 0 0]
- 切片的修改:
- 切片本身不具有数据,切片中的数据,都是底层的数组,也就是说,当切片中的数据发生修改时,底层的数组中值也会发生变化;
- 示例:
package main import ( "fmt" ) func main() { numa := [3]int{10, 12, 16} num1 := numa[:] num1[0] = 100 fmt.Println(num1) fmt.Println(numa) }
// 打印内容:
[100 12 16]
[100 12 16]
- 追加切片元素:
- 切片因为由数组支持,且数组本身的长度是固定的;所以,在切片追加元素的时候,实际创建了一个新的数组,并且将旧的数组的值复制到新的数组中,同时将切片的容量也翻了一倍
- append(切片,数据); 有返回值,需要被接收,不然会报错
- 示例:
package main import ( "fmt" ) func main() { numa := [3]int{10, 12, 16} num1 := numa[:] num1[0] = 100 fmt.Println(num1, cap(num1)) num1 = append(num1, 17) fmt.Println(num1, cap(num1)) } // 打印内容: [100 12 16] 3 [100 12 16 17] 6
- 多维切片:
package main import ( "fmt" ) func main() { pls := [][]string { {"C", "C++"}, {"JavaScript"}, {"Go", "Rust"}, } for _, v1 := range pls { for _, v2 := range v1 { fmt.Printf("%s ", v2) } fmt.Printf("\n") } } // 打印内容: C C++ JavaScript Go Rust
- 内存优化:
-问题描述:
切片持有对底层数组的引用;
只要切片在内存中,数组就不能被垃圾回收。
当数组很大,但是我们只需要利用一部分的数据做切片;
这时候的数组不会被回收,很占用内容;
- 解决方法:
- 利用 copy() 备份需要使用的切片:
- 示例:
1 package main 2 3 import ( 4 "fmt" 5 ) 6 7 func countries() []string { 8 countries := []string{"USA", "Singapore", "Germany", "India", "Australia"} 9 neededCountries := countries[:len(countries)-2] 10 countriesCpy := make([]string, len(neededCountries)) 11 copy(countriesCpy, neededCountries) //copies neededCountries to countriesCpy 12 return countriesCpy 13 } 14 func main() { 15 countriesNeeded := countries() 16 fmt.Println(countriesNeeded) 17 } 18 19 // 解析: 20 neededCountries := countries[:len(countries)-2 创建一个去掉尾部 2 个元素的切片 countries 21 11 行,将 neededCountries 复制到 countriesCpy 同时在函数的下一行返回 countriesCpy 22 现在 countries 数组可以被垃圾回收, 因为 neededCountries 不再被引用。