5 Go容器:数组Array、切片Slice、映射Map

一、数组Array

Go语言数组是一个定长的序列,其内部元素一般限定同一类型。数组为切片的底层结构。

    //创建数组1:声明后赋值
    var arr1 [3]int
    arr1[0] = 0
    arr1[1] = 1
    arr1[2] = 2
    
    //创建数组2:创建并初始化
    arr2 := [3]int{1, 3, 5}
    
    //创建数组3:自动计算数组长度
    arr3 := [...]int{2, 4, 6, 8, 10}
    
    //len()为内置函数,用来获取数组、切片、字符串、通道(channel)等的长度
    var count = len(arr3)
    fmt.Printf("类型为%T,值为%v,元素个数为%d\n", arr3, arr3, count)
    //类型为[6]int,值为[1 2 3 4 5 6],元素个数为6

    //声明一个两行三列的二维数组
    var grid [2][3]int
    grid[0] = [3]int{1,3,5}
    grid[1] = [3]int{2,4,6}
    fmt.Println(grid)    //[[1 3 5] [2 4 6]]
  
    


    //遍历数组
    for index, value := range arr1 {
        fmt.Println(index, " ", value)
    }
    //0   0
    //1   1
    //2   2
    //3   3
    //4   4

二、切片Slice

1.切片声明及初始化
//array[start:end]从数组身上截取下标为[start,end)片段,形成切片
//start代表开始下标,不写默认代表从头开始切
//end代表结束下标(本身不被包含),不写默认截取到末尾
func BaseSlice01() {
    //声明并初始化一个数组
    var arr = [...]int{0, 1, 2, 3, 4, 5, 6, 7}
    
    //基于数组创建切片
    slice1 := arr[2:5]
    slice2 := arr[:5]
    slice3 := arr[6:]
    slice4 := arr[:]

    fmt.Printf("arr类型为%T,值为%v\n", arr, arr)  //arr类型为[8]int,值为[0 1 2 3 4 5 6 7]  
    fmt.Printf("slice1类型为%T,值为%v\n", slice1, slice1)   //slice1类型为[]int,值为[2 3 4]
    fmt.Printf("slice2类型为%T,值为%v\n", slice2, slice2)  //slice2类型为[]int,值为[0 1 2 3 4]
    fmt.Printf("slice3类型为%T,值为%v\n", slice3, slice3)  //slice3类型为[]int,值为[6 7]
    fmt.Printf("slice4类型为%T,值为%v\n", slice4, slice4)  //slice4类型为[]int,值为[0 1 2 3 4 5 6 7]

}
2.切片追加元素
//向切片中追加元素
func BaseSlice02() {
    slice := []int{1, 2, 3, 4}
    slice = append(slice, 5, 6) //直接追加多个元素
    slice = append(slice, []int{7, 8, 9}...) //追加另一个切片里的元素,需将被追加元素“拍扁”,后面加...即可
    fmt.Printf("slice1类型为%T,值为%v,长度为%d,容量为%d\n", slice, slice, len(slice), cap(slice))
    ////slice1类型为[]int,值为[1 2 3 4 5 6 7 8 9],长度为9,容量为16

    //遍历切片
    for _, v := range slice {
        fmt.Print(v, " ")
    }
    //1 2 3 4 5 6 7 8 9 
}
3.切片容量扩展
//cap(slice)获得切片的容量
//创建之初,容量等于长度
//扩张时,一旦容量无法满足需要,就以翻倍的策略扩容
//注意切片扩容时其栈地址的变化
func BaseSlice03() {
    arr := [4]int{1, 2, 3, 4}
    fmt.Printf("arr类型为%T,值为%v,长度为%d,容量为%d,地址为%p\n", arr, arr, len(arr), cap(arr), &arr)

    slice := arr[:]
    fmt.Printf("slice类型为%T,值为%v,长度为%d,容量为%d,地址为%p\n", slice, slice, len(slice), cap(slice), slice)

    slice = append(slice, 5)
    fmt.Printf("slice类型为%T,值为%v,长度为%d,容量为%d,地址为%p\n", slice, slice, len(slice), cap(slice), slice)
    slice = append(slice, 6)
    fmt.Printf("slice类型为%T,值为%v,长度为%d,容量为%d,地址为%p\n", slice, slice, len(slice), cap(slice), slice)
    slice = append(slice, 7)
    fmt.Printf("slice类型为%T,值为%v,长度为%d,容量为%d,地址为%p\n", slice, slice, len(slice), cap(slice), slice)
    slice = append(slice, 8)
    fmt.Printf("slice类型为%T,值为%v,长度为%d,容量为%d,地址为%p\n", slice, slice, len(slice), cap(slice), slice)
    slice = append(slice, 9)
    fmt.Printf("slice类型为%T,值为%v,长度为%d,容量为%d,地址为%p\n", slice, slice, len(slice), cap(slice), slice)
    slice = append(slice, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)
    fmt.Printf("slice类型为%T,值为%v,长度为%d,容量为%d,地址为%p\n", slice, slice, len(slice), cap(slice), slice)
}

//arr类型为[4]int,值为[1 2 3 4],长度为4,容量为4,地址为0xc00011e000
//slice类型为[]int,值为[1 2 3 4],长度为4,容量为4,地址为0xc00011e000
//slice类型为[]int,值为[1 2 3 4 5],长度为5,容量为8,地址为0xc000126040
//slice类型为[]int,值为[1 2 3 4 5 6],长度为6,容量为8,地址为0xc000126040
//slice类型为[]int,值为[1 2 3 4 5 6 7],长度为7,容量为8,地址为0xc000126040
//slice类型为[]int,值为[1 2 3 4 5 6 7 8],长度为8,容量为8,地址为0xc000126040
//slice类型为[]int,值为[1 2 3 4 5 6 7 8 9],长度为9,容量为16,地址为0xc0000c0180
//slice类型为[]int,值为[1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20],长度为20,容量为32,地址为0xc00012c000

//每次切片扩容都是容量倍增,且其切片头指针地址都发生变化,可见倍增时是另开辟内存并复制
4.切片兼并
/*兼并另一个切片*/
func BaseSlice04() {
    slice := []int{1, 2, 3, 4}
    fmt.Printf("slice1类型为%T,值为%v,长度为%d,容量为%d\n", slice, slice, len(slice), cap(slice))

    sli := []int{7, 8, 9}

    slice = append(slice, sli...) //兼并时注意“拍扁”
    fmt.Printf("slice1类型为%T,值为%v,长度为%d,容量为%d\n", slice, slice, len(slice), cap(slice))

}

//slice1类型为[]int,值为[1 2 3 4],长度为4,容量为4
//slice1类型为[]int,值为[1 2 3 4 7 8 9],长度为7,容量为8
5.使用make()函数创建切片
/*创建指定长度和容量的切片*/
func BaseSlice05() {
    //创建空切片
    slice1 := make([]int, 0)
    fmt.Printf("slice1类型为%T,值为%v,长度为%d,容量为%d\n", slice1, slice1, len(slice1), cap(slice1))
    //slice1类型为[]int,值为[],长度为0,容量为0


    //创建空切片,但指定容量
    slice2 := make([]int, 0, 5)
    fmt.Printf("slice2类型为%T,值为%v,长度为%d,容量为%d\n", slice2, slice2, len(slice2), cap(slice2))
    //slice2类型为[]int,值为[],长度为0,容量为5


    //创建长度为3容量为5的切片,默认长度的元素都初始化为零值
    slice3 := make([]int, 3, 5)
    fmt.Printf("slice3类型为%T,值为%v,长度为%d,容量为%d\n", slice3, slice3, len(slice3), cap(slice3))
    //slice3类型为[]int,值为[0 0 0],长度为3,容量为5


    //创建长度为5的切片,默认长度的元素都初始化为零值,未指定容量时容量值等于长度值
    slice4 := make([]int, 5)
    fmt.Printf("slice4类型为%T,值为%v,长度为%d,容量为%d\n", slice4, slice4, len(slice4), cap(slice4))
    //slice4类型为[]int,值为[0 0 0 0 0],长度为5,容量为5
}
6.删除切片元素

go没有内置的切片删除函数,所以删除元素需要遍历切片

//删除切片元素
func RmSliceElement(slice []interface{}, index int) []interface{} {
    if index > len(slice)-1 {
        panic("index out of range")
    }

    for _, v := range slice {
        //如果是最后一个元素
        if index == len(slice)-1 {
            return slice[:index]
        }
        //如果是中间元素
        if v == slice[index] {
            return append(slice[:index], slice[index+1:]...)
        }
    }

    return nil
}

三、映射Map

1.map的创建
    //创建并初始化
    m1 := map[string]string{
        "name":    "fun",
        "age":  "18",
        "gender":    "male",
    }
    
    //创建一个长度为5的空map,已分配内存
    m2 := make(map[string]int,5)

    //只声明一个map,未分配内存
    var m3 map[string]int 
    
    fmt.Printf("m1:%v,地址为%p\n", m1,m1)
    fmt.Printf("m2:%v,地址为%p\n", m2,m2)
    fmt.Printf("m3:%v,地址为%p\n", m3,m3)
    //m1:map[age:18 gender:male name:fun],地址为0xc00008e480
    //m2:map[],地址为0xc00008e4b0
    //m3:map[],地址为0x0
2.map的遍历及访问值
    //遍历
    m := map[string]string{
        "name":    "fun",
        "age":  "18",
        "gender":    "male",
    }
    for key, val := range m {
        fmt.Printf("Key:%s,Value:%v\n", key, val)
    }
    //Key:name,Value:fun
    //Key:age,Value:18
    //Key:gender,Value:male

    //访问一个键值
    name := m["name"]
    fmt.Printf("name:%v\n", name)
    //name:fun
    
    //访问一个不存在的键
    names := m["names"]
    fmt.Printf("names:%v\n", names)
    //names:

    //带校验的访问
    if a, ok := m["name"]; !ok {
        fmt.Println("key is not exist!")
    } else {
        fmt.Println(a)
    }
3.map删除元素

delete()为内置函数,只对map有效

func BaseMap03() {
    m := map[string]string{
        "name":   "fun",
        "age":    "18",
        "gender": "male",
    }

    fmt.Printf("m:%v\n", m)

    delete(m, "age")
    fmt.Printf("m:%v\n", m)

}
//m:map[age:18 gender:male name:fun]
//m:map[gender:male name:fun]
4.多维map的使用
//多维map的使用
func BaseMap04() {
    //声明二维map的结构
    var ms = make(map[string]map[string]int, 3)
    fmt.Printf("ms:Type:%T, Value:%v, Len:%d, Address:%p\n", ms, ms, len(ms), ms)

    //为二维map的内部初始化
    ms["a"] = make(map[string]int, 4)
    ms["b"] = make(map[string]int, 5)
    ms["c"] = make(map[string]int, 6)
    fmt.Printf("ms:Type:%T, Value:%v, Len:%d, Address:%p\n", ms, ms, len(ms), ms)

    ms["d"] = make(map[string]int, 7)
    fmt.Printf("ms:Type:%T, Value:%v, Len:%d, Address:%p\n", ms, ms, len(ms), ms)

    //赋值:只有二维数组内部初始化后才能赋值
    //否则报panic: assignment to entry in nil map
    ms["a"]["aa"] = 1
    ms["a"]["bb"] = 2
    ms["a"]["cc"] = 3
    fmt.Printf("ms:Type:%T, Value:%v, Len:%d, Address:%p\n", ms, ms, len(ms), ms)

//[1]ms:Type:map[string]map[string]int, Value:map[], Len:0, Address:0xc000104300
//[2]ms:Type:map[string]map[string]int, Value:map[a:map[] b:map[] c:map[]], Len:3, Address:0xc000104300
//[3]ms:Type:map[string]map[string]int, Value:map[a:map[] b:map[] c:map[] d:map[]], Len:4, Address:0xc000104300
//[4]ms:Type:map[string]map[string]int, Value:map[a:map[aa:1 bb:2 cc:3] b:map[] c:map[] d:map[]], Len:4, Address:0xc000104300
}

四、关于make()和new()函数

1.make()

Go为创建切片slice、映射map、通道chan内建一个函数,即make()。

用法:

func make(Type, size IntegerType) Type

根据官方文档:

  • 参数一为类型,非值,返回类型与其参数相同
  • 参数二不同类型传参不同

切片:size指定了其长度。该切片的容量等于其长度。切片支持第二个整数实参可用来指定不同的容量;它必须不小于其长度,因此 make([]int, 0, 10) 会分配一个长度为0,容量为10的切片。

映射:初始分配的创建取决于size,但产生的映射长度为0。size可以省略,这种情况下就会分配一个小的起始大小。

通道:通道的缓存根据指定的缓存容量初始化。若 size为零或被省略,该信道即为无缓存的。

2.new()

用法:

func new(Type) *Type

内建函数new分配内存。其第一个实参为类型,而非值。其返回值为指向该类型的新分配的零值的指针。

3.两个函数的区别
  • make()是专门用来创建切片、映射、通道的,为其分配内存并初始化零值,返回的切片、映射、通道天生就是指针地址。
  • new()一般是为创建除内置引用类型外的其他任意类型分配内存的,但不会初始化数据,只会把内存置零,返回的是*T指针地址。一般用于创建array或struct值类型,当然你也可以用它创建基础数据类型返回指针地址。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,384评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,845评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,148评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,640评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,731评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,712评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,703评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,473评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,915评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,227评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,384评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,063评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,706评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,302评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,531评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,321评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,248评论 2 352

推荐阅读更多精彩内容