这篇讲解go语言中数据存储类型array、slice、map和struct,要清楚它们那些是值传递,那些是指针传递(也就是引用类型),这对后面的数据处理非常重要!
1.数组Array
定义数组的格式:
var <varName> [n]<type>
示例如下:
package main
import "fmt"
func main() {
//先声明,后赋值
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)
}
数组需要注意的点:
- 1、数组长度也是类型的一部分,因此具有不同长度的数组为不同类型;
- 2、注意区分指向数组的指针和指针数组;
- 3、数组在Go中为值类型;(这是和Java非常不同的一点)
- 4、数组之间可以使用
==
或者!=
进行比较,但不可以使用<
或>
- 5、可以使用
new
来创建数组,此方法返回一个指向数组的指针; - 6、Go支持多维数组
区分指向数组的指针和指针数组
package main
import "fmt"
func main() {
//可以利用...不指定数组长度
a:=[...]int{9:1}
//指向数组的指针
var p *[10]int=&a
fmt.Print(p)
}
package main
import "fmt"
func main() {
x,y :=1,2
//指针数组
a:=[...]*int{&x,&y}
fmt.Print(a)
}
2.切片slice(重点)
切片需要注意的点:
- 1、其本身并不是数组,它指向底层的数组。(切片为引用类型);
- 2、作为变长数组的替代方案,可以关联底层数组的局部或全部;
- 3、可以直接创建或从底层数组获取生成;
- 4、一般使用
make()
创建,使用len()
获取元素个数,cap()
获取容量; - 5、如果多个slice指向相同的底层数组,其中一个的值改变会影响全部。
Slice
的第一种创建方式:
package main
import "fmt"
func main() {
//创建一个切片,跟数组相比没有规定长度
p:=[]int{2,3,5,7,11,13}
fmt.Println("p==",p)
for i := 0; i < len(p); i++ {
fmt.Println(i,p[i])
}
}
Slice
的第二种创建方式:
package main
import "fmt"
func main() {
//利用make创建包含3个元素,容量为10的slice
//提前分配容量10是为了增加元素而在此分配内存
s1:=make([]int,3,10)
fmt.Println(len(s1),cap(s1))
//注意打印出来的时候只包含三个元素
fmt.Println(s1)
}
Slice
与底层数组的对应关系:
Reslice
的理解:
- 1、
Reslice
时索引以被slice的切片为准; - 2、索引不可以超过被slice的切片的容量cap()值;
- 3、索引越界不会导致底层数组的重新分配而引发错误。
package main
import "fmt"
func main() {
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))
}
Append
的理解:
- 1、可以在
slice
尾部追加元素; - 2、可以将一个
slice
追加在另一个slice
尾部; - 3、如果最终长度未超过追加到slice的容量则返回原始
slice
,如果超过追加到的slice的容量则将重新分配数组并拷贝原始数据。
package main
import "fmt"
func main() {
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)
}
3.map
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创建的方式一:
package main
import "fmt"
func main() {
//声明一个map,int是key类型,string是value类型
var m map[int]string
//初始化map
m =map[int]string{}
fmt.Print(m)
}
map创建的方式二:
package main
import "fmt"
func main() {
//使用make创建一个map
m:=make(map[int]string)
m[1]="OK"
a:=m[1]
fmt.Println(m)
fmt.Print(a)
}
通过range
对map
进行初始化操作如下:
package main
import "fmt"
func main() {
//创建一个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)
}
使用map作为函数的参数传递:引用传递
package main
import "fmt"
func main() {
a:=map[string]int{
"Michaeljian":22,
"Jerry":20,
}
mapTest(a)
fmt.Print(a["Michaeljian"])
}
//将map作为参数传递进去并修改对应的值
func mapTest(b map[string]int) {
b["Michaeljian"] = 18
}
4.struct
struct
的知识要点:
- 1、Go中的struct与C中的struct非常相似,并且Go没有class,使用
type<Name>struct{}
定义结构,名称遵循可见性规则; - 2、支持指向自身的指针类型成员,支持匿名结构,可用作成员或定义成员变量,匿名结构也可以用于
map
值; - 3、可以使用字面值对结构进行初始化,允许直接通过指针来读写结构成员;
- 4、相同类型的成员可进行直接拷贝赋值;
- 5、支持
==
与!=
比较运算,但不支持>
或<
; - 6、支持匿名字段,本质上是定义了以某种类型名为名称的字段;
- 7、可以使用匿名字段指针;
- 8、嵌入结构作为匿名字段看起来像继承,但不是继承。
struct
的定义和赋值:
package main
import "fmt"
type person struct {
Name string
Age int
}
func main() {
a:=person{}
a.Name ="Michaeljian"
a.Age =25
fmt.Print(a)
}
使用struct作为函数传递的参数:值传递
package main
import "fmt"
type person struct {
Name string
Age int
}
func main() {
//字面值初始化
a:=person{
Name:"Michaeljian",
Age:25,
}
fmt.Println(a)
A(a)
fmt.Println(a)
}
func A(p person) {
p.Age = 12
fmt.Println("A",p)
}
如何想要将struct
作为引用传递,需要借助指针:
package main
import "fmt"
type person struct {
Name string
Age int
}
func main() {
//字面值初始化
a:=person{
Name:"Michaeljian",
Age:25,
}
/**
//一般在开发中会采用这种的方式,直接将person的地址赋值给a
a:= &person{
Name:"Michaeljian",
Age:25,
}
//此时注意要修改Name的值话直接采用下面的方式即可。
a.Name="Jerry"
*/
fmt.Println(a)
//传入struct的地址
A(&a)
fmt.Println(a)
}
func A(p *person) {
p.Age = 12
fmt.Println("A",p)
}
使用匿名结构类型声明struct
,格式如下:
package main
import "fmt"
func main() {
//可以a:=&struct{}{}做地址传递
a:= struct {
Name string
Age int
}{
Name:"Michaeljian",
Age:19,
}
fmt.Print(a)
}
使用struct
的嵌入结构,格式如下:
package main
import "fmt"
type human struct {
Sex int
}
type teacher struct {
human
Name string
Age int
}
type student struct {
human
Name string
Age int
}
func main() {
//如何给嵌入结构human中的字段赋值,human:human{Sex:1}
a:=teacher{Name:"Michaeljian",Age:25,human:human{Sex:1}}
b:=student{Name:"Jerry",Age:24,human:human{Sex:2}}
a.Name ="Jerry"
a.Sex = 100
fmt.Print(a,b)
}