路漫漫其修远兮
吾将上下而求索

第7章总结:数组与切片

第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)

未经允许不得转载:江哥架构师笔记 » 第7章总结:数组与切片

分享到:更多 ()

评论 抢沙发

评论前必须登录!