Go基础
数组:
数组是一种数据类型元素的组合。在Go语言中,数组从声明时就确定,使用的时候可以修改数组中的成员,但是数组的大小时不可以变化的
// 定义一个长度为3元素类型为int的数组a
var a [3]int
数组定义:var 数组变量名 [元素数量] T
数组的长度必须时常量,并且长度是数组类型的一部分。一旦定义了,长度不能变。[5]int和[10]int是不同的类型
数组可以通过下标进行访问,下标是从0开始的,最后一个元素的下标是len-1,访问越界(下标在合法的范围之外),则会触发访问越界,会panic
数组的初始化:
1、初始化数组时可以使用初始化列表来设置数组元素的值
func main() { var testArray [3]int //数组会初始化为int类型的零值 var numArray = [3]int{1, 2} //使用指定的初始值完成初始化 var cityArray = [3]string{"北京", "上海", "深圳"} //使用指定的初始值完成初始化 fmt.Println(testArray) //[0 0 0] fmt.Println(numArray) //[1 2 0] fmt.Println(cityArray) //[北京 上海 深圳] }
2、按照上面的方法每次都要确保提供的初始值和数组长度一致,一般情况下我们可以让编译器根据初始值的个数自行推断数组的长度
func main() { var testArray [3]int var numArray = [...]int{1, 2} var cityArray = [...]string{"北京", "上海", "深圳"} fmt.Println(testArray) //[0 0 0] fmt.Println(numArray) //[1 2] fmt.Printf("type of numArray:%T\n", numArray) //type of numArray:[2]int fmt.Println(cityArray) //[北京 上海 深圳] fmt.Printf("type of cityArray:%T\n", cityArray) //type of cityArray:[3]string }
3、我们还可以使用指定索引值的方式来初始化数组
func main() { a := [...]int{1: 1, 3: 5} fmt.Println(a) // [0 1 0 5] fmt.Printf("type of a:%T\n", a) //type of a:[4]int }
遍历数组:
func main() { var a = [...]string{"北京", "上海", "深圳"} // 方法1:for循环遍历 for i := 0; i < len(a); i++ { fmt.Println(a[i]) } // 方法2:for range遍历 for index, value := range a { fmt.Println(index, value) } }
多维数组:
Go语言是支持多维数组的,我们这里以二维数组为例(数组中嵌套数组)
二维数组的定义:
func main() { a := [3][2]string{ {"北京", "上海"}, {"广州", "深圳"}, {"成都", "重庆"}, } fmt.Println(a) //[[北京 上海] [广州 深圳] [成都 重庆]] fmt.Println(a[2][1]) //支持索引取值:重庆 }
二维数组的遍历
func main() { a := [3][2]string{ {"北京", "上海"}, {"广州", "深圳"}, {"成都", "重庆"}, } for _, v1 := range a { for _, v2 := range v1 { fmt.Printf("%s\t", v2) } fmt.Println() } }
slice:
在很多应用场景中,数组并不能满足我们的需求。在刚开始定义数组的时候,我们并不知道需要多大的数组,所以这时候我们就需要动态数据了,在Go语言中称这种数据结构叫slice
slice并不是真正意义上的动态数组,而是一个引用类型。slice总是指向一个底层的array,slice的声明也可以像array一样,只是不需要长度
// 和声明array一样,只是少了长度 var fslice []int
slice := []byte {'a', 'b', 'c', 'd'}
slice可以从数组或者一个已经存在的slice中再次声明。slice通过array[i:j]来获取,其中i是数组的起始位置,j就是结束位置,但是不包含array[j],它的长度是j-1
// 声明一个含有10个元素元素类型为byte的数组 var ar = [10]byte {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'} // 声明两个含有byte的slice var a, b []byte // a指向数组的第3个元素开始,并到第五个元素结束, a = ar[2:5] //现在a含有的元素: ar[2]、ar[3]和ar[4] // b是数组ar的另一个slice b = ar[3:5] // b的元素是:ar[3]和ar[4]
ps:slice和数组在声明的时候的区别:声明数组的时候,方括号内写明了数组的长度使用...自动计算长度,而声明slice的时候,方括号里面是没有任何字符的
- slice中默认的起始位置是0, ar[:n]等于ar[0:n]
- slice的第二个序列默认是数组的长度,ar[n:]等价于ar[n:len(ar)]
- 如果一个数组里面直接获取slice,可以使用ar[:],因为默认第一个序列是0,第二个是数组的长度
slice是引用类型,所以当引用改变其中元素的值的时候,其它的所有引用都会作出相应的改变,从概念上来说slice更像是一个结构体,这个结构体主要包含了指针、长度以及最大长度
指针:指向数组中slice指定的开始位置
长度:slice的长度
最大长度:也就是slice开始位置到数组的最后位置的长度
slice中的内置函数:
len:获取slice中的长度
cap:获取slice的最大容量
append:像slice中追加一个或者多个元素,然后返回一个和slice一样类型的slice
copy:从源slice的src中赋值元素到目标的dst,并且返回复制的元素的个数
ps:append函数会改变slice所引用的数组的内容,从而影响到引用同一个数组的其它的slice。但当slice没有剩余空间的(cap-len) == 0的时候,此时将动态分配新的数组的空间。返回slice数组指针将指向这个空间,而原数组的内容将保持不变;其它引用此数组的slice则不受影响
从Go1.2开始slice支持了三个参数的slice,之前我么一直采用的这种方式在slice或者array基础上来产生一个新的slice
map:
map也就是python中的字典的概念格式是:map[keyType]valueType
map的读取和设置也类似slice一样,通过key来进行操作,只是slice的index只能是int类型,而map多了很多类型,可以是int,可以是string以及所有完全定义了==和!=操作的类型
// 声明一个key是字符串,值为int的字典,这种方式的声明需要在使用之前使用make初始化 var numbers map[string]int // 另一种map的声明方式 numbers = make(map[string]int) numbers["one"] = 1 //赋值 numbers["ten"] = 10 //赋值 numbers["three"] = 3 fmt.Println("第三个数字是: ", numbers["three"]) // 读取数据 // 打印出来如:第三个数字是: 3
这个map就像我们平常看到的表格一样,左边一列是key,右边一列是值
使用map的过程中需要注意的点:
- map是无序的,每次打印出来的map是会不一样的,他不能通过index来取值,而必须通过对应key来获取
- map的长度是不固定的,也就是和slice一样的,也是一种引用类型
- 内置的len函数同样适用于map,返回的是map拥有的key的数量
- map的值可以很方便的进行修改,通过对应的key就可以直接改成你想要的内容了,这个和python中的字典很类似
map的初始化可以通过key:value的方式来进行,通过map内置有判断是否存在key的方式
通过delete删除map的元素
// 初始化一个字典 rating := map[string]float32{"C":5, "Go":4.5, "Python":4.5, "C++":2 } // map有两个返回值,第二个返回值,如果不存在key,那么ok为false,如果存在ok为true csharpRating, ok := rating["C#"] if ok { fmt.Println("C# is in the map and its rating is ", csharpRating) } else { fmt.Println("We have no rating associated with C# in the map") } delete(rating, "C") // 删除key为C的元素
由于map是一种引用类型,如果两个map同时指向一个底层,那么一个改变了,另一个也会随之改变
m := make(map[string]string) m["Hello"] = "Bonjour" m1 := m m1["Hello"] = "Salut" // 现在m["hello"]的值已经是Salut了
make、new操作:
make用于内建类型(map、slice、channel)的内存分配。new用于各种类型的内存的分配
内建函数new本质上跟其它语言中的同名函数功能一样:new(T)分配了一个零值填充的T类型的内存空间,并且返回一个内存地址。用Go的术语来说就是返回了一个指针,指向了新分配的类型T的零值
new返回指针
内建函数make(T, args)与new(T)有着不同功能,make只能创建slice、map和channel,并且返回一个有初始值(非零)的T类型,而不是*T。本质上来讲,导致这三个类型有所不同的原因是指向数据结构的引用在使用前必须被初始化。比如一个slice是一个包含指向数据结构(内部包含array)的指针、长度以及最大容量的三个描述符;在这些项目被初始化之前,slice为nil,对于slice、map、channel来说,make初始化了内部数据结果,填充了适当的值
make返回初始化后的(非零)值
零值
关于"零值",所指并非是空值,而是一种变量未填充以前的默认值,通常是0。