–
第7章 7.1 下面的内容介绍:数组、切片和 map(就是python中的字典) 数组是具有相同 唯一类型 的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整形、字符串或者自定义类型 数组长度必须是一个常量表达式 数组长度最大为 2Gb 声明的格式是: var identifier [len]type 例如: var arr1 [5]int 每个元素是一个整形值,当声明数组时所有的元素都会被自动初始化为默认值 0。 arr1 的长度是 5,索引范围从 0 到 len(arr1)-1。 遍历 for i:=0; i < len(arr1); i++{ arr1[i] = ... } 或者 for i,v:= range arr1 { #range返回两个值,一个是索引号,一个是对应的值 ... } 在函数中数组作为参数传入时,如 func1(arr2),会产生一次数组拷贝,func1 方法不会修改原始的数组 arr2 如果想修改原数组,那么 arr2 必须通过&操作符以引用方式传过来,例如 func1(&arr2) Go 语言中的数组是一种 值类型(不像 C/C++ 中是指向首元素的指针),所以可以通过 new() 来创建: var arr1 = new([5]int)。 func f(a [3]int) { fmt.Println(a) } func fp(a *[3]int) { fmt.Println(a) } #注意这里的形参,*[3]int func main() { var ar [3]int f(ar) // passes a copy of ar fp(&ar) // passes a pointer to ar } 可以通过 数组常量 的方法来初始化数组,而不用依次使用 []= 方法 第一种变化: var arrAge = [5]int{18, 20, 15, 22, 16} 注意 [5]int 可以从左边起开始忽略:[10]int {1, 2, 3} :这是一个有 10 个元素的数组,除了前三个元素外其他元素都为 0。 第二种变化: var arrLazy = [...]int{5, 6, 7, 8, 22} ... 可同样可以忽略,从技术上说它们其实变化成了切片。 第三种变化:key: value syntax var arrKeyValue = [5]string{3: "Chris", 4: "Ron"} 只有索引 3 和 4 被赋予实际的值,其他元素都被设置为空的字符串, 数组别名: type Vector3D [3]float32 var vec Vector3D 数组通常是一维的,但是可以用来组装成多维数组,例如:[3][5]int,[2][2][2]float64。 把一个大数组传递给函数会消耗很多内存。有两种方法可以避免这种现象: 传递数组的指针 使用数组的切片 func main(){ array := [3]float64{7.0, 8.0, 9.0} x := Sum(&array) fmt.Printf("is %f\n", x) } func Sum(a *[3]float64) (sum float64){ for _, v := range a{ sum += v } return } 7.2 切片是一个指针,引用 切片(slice)是对数组一个连续片段的引用(该数组我们称之为相关数组,通常是匿名的),所以切片是一个引用类型(因此更类似于 C/C++ 中的 数组类型,或者 Python 中的 list 类型)。这个片段可以是整个数组,或者是由起始和终止索引标识的一些项的子集。需要注意的是,终止索引标 识的项不包括在切片内。切片提供了一个相关数组的动态窗口。 len() 函数获取切片的长度 切片的长度可以在运行时修改,最小为 0 最大为相关数组的长度:切片是一个 长度可变的数组。 cap() 可以测量切片最长可以达到多少:它等于切片的长度 + 数组除切片之外的长度。 如果 s 是一个切片,cap(s) 就是从 s[0] 到数组末尾的数组长度。切片的长度永远不会超过它的容量,所以对于 切片 s 来说该不等式永远成立:0 <= len(s) <= cap(s) 优点 因为切片是引用,所以它们不需要使用额外的内存并且比使用数组更有效率,所以在 Go 代码中 切片比数组更常用。在函数传递中,相当于传递的是指针 声明切片的格式是: var identifier []type(不需要说明长度)。 一个切片在未初始化之前默认为 nil,长度为 0。 切片的初始化格式是:var slice1 []type = arr1[start:end]。 var slice1 []type = arr1[:] 那么 slice1 就等于完整的 arr1 数组,或者:另外一种表述方式是:slice1 = &arr1 arr1[:3] 和 arr1[0:3] 相同,包含了从第一个到第三个元素(不包括第三个) 如果想去掉 slice1 的最后一个元素,只要 slice1 = slice1[:len(slice1)-1] 一个由数字 1、2、3 组成的切片可以这么生成:s := [3]int{1,2,3}[:] 甚至更简单的 s := []int{1,2,3}。 下面的关系总成立 s == s[:i] + s[i:] // i是一个整数且: 0 <= i <= len(s) len(s) <= cap(s) 切片在内存中的组织方式实际上是一个有 3 个域的结构体:指向相关数组的指针,切片 长度以及切片容量 使用切片传递到函数 func sum(a []int) int{ s := 0 for i:=0; i<len(a); i++{ s += a[i] } return s } func main(){ a := [5]int{0,1,2,3,4} s := sum(a[:]) #此处使用切片 fmt.Printf("sum %d", s) } 当相关数组还没有定义时,我们可以使用 make() 函数来创建一个切片 同时创建好相关数组:var slice1 []type = make([]type, len)。 也可以简写为 slice1 := make([]type, len),这里 len 是数组的长度并且也是 slice 的初始长度。 所以定义 s2 := make([]int, 10),那么 cap(s2) == len(s2) == 10。 make 接受 2 个参数:元素的类型以及切片的元素个数。 如果你想创建一个 slice1,它不占用整个数组,而只是占用以 len 为个数个项,那么只要:slice1 := make([]type, len, cap)。 make 的使用方式是:func make([]T, len, cap),其中 cap 是可选参数。 所以下面两种方法可以生成相同的切片: make([]int, 50, 100) new([100]int)[0:50] 7.3 数组的内容可以是字符串 seasons := []string{"Spring", "Summer", "Autumn", "Winter"} 7.4 切片创建的时候通常比相关数组小,例如: slice1 := make([]type, start_length, capacity) 其中 start_length 作为切片初始长度而 capacity 作为相关数组的长度。 这么做的好处是我们的切片在达到容量上限后可以扩容。改变切片长度的过程称之为切片重组 reslicing,做法如下:slice1 = slice1[0:end],其 中 end 是新的末尾索引(即长度)。 7.5 如果想增加切片的容量,我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来。 下面的代码描述了从拷贝切片的 copy 函数和向切片追加新元素的 append 函数。 package main import "fmt" func main() { sl_from := []int{1, 2, 3} sl_to := make([]int, 10) n := copy(sl_to, sl_from) #这里from在后面 fmt.Println(sl_to) fmt.Printf("Copied %d elements\n", n) // n == 3 sl3 := []int{1, 2, 3} sl3 = append(sl3, 4, 5, 6) fmt.Println(sl3) } func append(s[]T, x ...T) []T 其中 append 方法将 0 个或多个具有相同类型 s 的元素追加到切片后面并且返回新的切片;追加的元素必须和原切片的元素同类型。 如果 s 的容量不足以存储新增元素,append 会分配新的切片来保证已有切片元素和新增元素的存储。因此,返回的切片可能已经"指向一个不同的相关数组了"。 func copy(dst, src []T) int copy 方法将类型为 T 的切片从源地址 src 拷贝到目标地址 dst,覆盖 dst 的相关元素,并且返回拷贝的元素个数。 源地址和目标地址可能会有重叠。 7.6 使用 substr := str[start:end] 可以从字符串 str 获取到从索引 start 开始到 end-1 位置的子字符串。同样的,str[start:] 则表示获取从 start 开始到 len(str)-1 位置的子字符串。而 str[:end] 表示获取从 0 开始到 end-1 的子字符串。 Go 语言中的字符串是不可变的,也就是说 str[index] 这样的表达式是不可以被放在等号左侧的。如果尝试运行 str[i] = 'D' 会得到错误:cannot assign to str[i]。 因此,您必须先将字符串转换成字节数组,然后再通过修改数组中的元素值来达到修改字符串的目的,最后将字节数组转换回字符串格式。 修改字符串中的某个字符 将字符串 "hello" 转换为 "cello": s := "hello" c := []byte(s) c[0] = ’c’ s2 := string(c) // s2 == "cello" 所以,您可以通过操作切片来完成对字符串的操作。 字符串中的每个字母可以单独存到数组中,互相转换。当函数需要传入多个参数时,可以将数组传入,就是多个参数 将切片 b 的元素追加到切片 a 之后:a = append(a, b...) 复制切片 a 的元素到新的切片 b 上: b = make([]T, len(a)) copy(b, a) 删除位于索引 i 的元素:a = append(a[:i], a[i+1:]...) 切除切片 a 中从索引 i 至 j 位置的元素:a = append(a[:i], a[j:]...) 为切片 a 扩展 j 个元素长度:a = append(a, make([]T, j)...) 在索引 i 的位置插入元素 x:a = append(a[:i], append([]T{x}, a[i:]...)...) 在索引 i 的位置插入长度为 j 的新切片:a = append(a[:i], append(make([]T, j), a[i:]...)...) 在索引 i 的位置插入切片 b 的所有元素:a = append(a[:i], append(b, a[i:]...)...) 取出位于切片 a 最末尾的元素 x:x, a = a[len(a)-1], a[:len(a)-1] 将元素 x 追加到切片 a:a = append(a, x)
–
评论前必须登录!
注册