PHPer的Go之路 -- array、slice、map和struct的对比
数组Array
1、数组长度也是类型的一部分,因此具有不同长度的数组为不同类型;
2、注意区分指向数组的指针和指针数组;
3、数组在Go中为值类型;(这是和Java非常不同的一点)
4、数组之间可以使用 == 或者 != 进行比较,但不可以使用 < 或 >
5、可以使用new来创建数组,此方法返回一个指向数组的指针;
6、Go支持多维数组
package main
import "fmt"
func main() {
arrayTest()
}
func arrayTest() {
//先声明,后赋值
var a [2] string
a[0] = "hello"
a[1] = "world"
fmt.Println(a[0], a[1])
fmt.Println(a)
//声明并赋值
prime := [6]int{2, 3, 5, 7, 11, 13}
fmt.Println(prime)
}
区分指向数组的指针和指针数组:
//可以利用...不指定数组长度
a:=[...]int{9:1}
//指向数组的指针
var p *[10]int=&a
fmt.Println(p)
x,y :=1,2
//指针数组
b:=[...]*int{&x,&y}
fmt.Println(b)
λ go run gogo.go
&[0 0 0 0 0 0 0 0 0 1]
[0xc000012168 0xc000012180]
切片slice
1、其本身并不是数组,它指向底层的数组。(切片为引用类型);
2、作为变长数组的替代方案,可以关联底层数组的局部或全部;
3、可以直接创建或从底层数组获取生成;
4、一般使用make()创建,使用len()获取元素个数,cap()获取容量;
5、如果多个slice指向相同的底层数组,其中一个的值改变会影响全部。
创建方式
//创建一个切片,跟数组相比没有规定长度
p:=[]int{2,3,5,7,11,13}
fmt.Println("p==",p)
for i := 0; i < len(p); i++ {
fmt.Println(i,p[i])
}
//利用make创建包含3个元素,容量为10的slice
//提前分配容量10是为了增加元素而在此分配内存
s1:=make([]int,3,10)
fmt.Println(len(s1),cap(s1))
//注意打印出来的时候只包含三个元素
fmt.Println(s1)
λ go run gogo.go
p== [2 3 5 7 11 13]
0 2
1 3
2 5
3 7
4 11
5 13
3 10
[0 0 0]
Reslice的理解:
1、Reslice时索引以被slice的切片为准;
2、索引不可以超过被slice的切片的容量cap()值;
3、索引越界不会导致底层数组的重新分配而引发错误。
a:=[]byte{'a','b','c','d','e','f','g','h','i','j','k'}
//前闭后开
sa:=a[2:5]
//转化为字符串
fmt.Println(string(sa))
fmt.Println(len(sa),cap(sa))
//从sa中取出元素--reslice功能
sb:=sa[3:5]
//打印出来的是fg,但是sa中并没有fg,这是为什么呢?
//sa的本质是指针,指向的数组的地址,它会包含从3到最后一个元素的地址。(就像图中阴影部分的元素)
fmt.Println(string(sb))
λ go ru
cde
3 9
fg
Append的理解:
1、可以在slice尾部追加元素;
2、可以将一个slice追加在另一个slice尾部;
3、如果最终长度未超过追加到slice的容量则返回原始slice,如果超过追加到的slice的容量则将重新分配数组并拷贝原始数据。
s1:=make([]int,3,6)
//按格式输出
fmt.Printf("%p\n",s1)
s1 =append(s1,1,2,3)
fmt.Printf("%v %p\n",s1,s1)
s1 =append(s1,1,2,3)
fmt.Printf("%v %p\n",s1,s1)
λ go run gogo.go
0xc00000e360
[0 0 0 1 2 3] 0xc00000e360
[0 0 0 1 2 3 1 2 3] 0xc000046060
集合map
1、类似其它语言中哈希表或者字典,以key-value形式存储数据;
2、Key必须是支持==或!=比较运算的类型,不可以是函数、map或slice;
3、Map查找比线性搜索快很多,但比使用索引访问数据的类型慢100倍;
4、Map使用make()创建,支持:=这种简写方式,make([keyType] valueType,cap),cap表示容量,可省略;超出容量时会自动扩容,但尽量提供一个合理的初始值;使用len()获取元素的个数。
5、键值对不存在时自动添加,使用delete()删除某键值对,比如delete(m,1);
6、使用for range对map和slice进行迭代操作。
创建
//声明一个map,int是key类型,string是value类型
var m map[int]string
//初始化map
m =map[int]string{}
fmt.Print(m)
//使用make创建一个map
n:=make(map[int]string)
n[1]="OK"
a:=n[1]
fmt.Println(n)
fmt.Print(a)
λ go run gogo.go
map[]map[1:OK]
OK
range操作
//创建一个slice
sm:=make([]map[int]string,5)
//对一个slice进行迭代,并对slice中的map进行初始化(i对代表的索引下标,v是代表对应的值)
for i:= range sm {
//注意是通过索引去改变map中的值,否则只是普通的值拷贝
sm[i] =make(map[int]string,1)
sm[i][1] ="OK"
fmt.Println(sm[i])
}
fmt.Println(sm)
λ go run gogo.go
map[1:OK]
map[1:OK]
map[1:OK]
map[1:OK]
map[1:OK]
[map[1:OK] map[1:OK] map[1:OK] map[1:OK] map[1:OK]]
结构体struct
1、Go中的struct与C中的struct非常相似,并且Go没有class,使用type<Name>struct{} 定义结构,名称遵循可见性规则;
2、支持指向自身的指针类型成员,支持匿名结构,可用作成员或定义成员变量,匿名结构也可以用于map值;
3、可以使用字面值对结构进行初始化,允许直接通过指针来读写结构成员;
4、相同类型的成员可进行直接拷贝赋值;
5、支持==与!=比较运算,但不支持 > 或 < ;
6、支持匿名字段,本质上是定义了以某种类型名为名称的字段;
7、可以使用匿名字段指针;
8、嵌入结构作为匿名字段看起来像继承,但不是继承。
package main
import "fmt"
type person struct {
Name string
Age int
}
func main() {
a:=person{}
a.Name ="臭大佬"
a.Age =26
fmt.Print(a)
}
λ go run gogo.go
{臭大佬 26}
值传递
package main
import "fmt"
type person struct {
Name string
Age int
}
func main() {
//字面值初始化
a:=person{
Name:"臭大佬",
Age:26,
}
fmt.Println(a)
A(a)
fmt.Println(a)
}
func A(p person) {
p.Age = 12
fmt.Println("A:",p)
}
λ go run gogo.go
{臭大佬 26}
A: {臭大佬 12}
{臭大佬 26}