Go学习笔记(9)Go容器类型——数组&切片&map

文章目录

  • 写在前面
  • 数组
  • 切片
  • Map

写在前面

    前面的文章介绍了Go的一些基本类型,本文开始涉及Go的一些容器类型,它们都是可以包含多个元素的数据结构,如数组、切片、map

数组

    数组是具有相同类型且长度固定的一组元素集合,定义的格式:var 数组名 [数组长度]数组元素类型,下面声明一个长度为5的int型数组arr

var arr [5]int
  • 数组元素可以是任意的原始类型,例如整型、字符串等,一个数组内所有元素的类型必须是相同的
  • 数组的长度必须是一个常量表达式,且是一个非负整数,特别注意数组的长度也是数组类型的一部分,因此不同长度的数组是不同的类型。数组的长度在声明时就要给出。获取数组长度用内置函数len(arr)
  • 当声明数组时所有的元素都会被初始化为默认的类型零值,我们也可以在声明数组的同时进行初始化
    var arr = [5]int{1, 2, 3, 4, 5}
    a := [3]int{1, 2, 3}
    
    还可以在初始化时仅初始化指定元素
    b := [3]int{1 : 2, 2 : 3}  //初始化索引为1的值为2,索引为2的值为3
    
  • 如果声明数组时不想直接写长度,可以用...代替,编译器会自动生成满足最低长度要求的数组
    c := [...]int{4 : 1}        //因为我们指定了索引为4的值为1,因此数值至少包含5个元素,这里将生成长度为5的数组
    
  • 通过for循环来遍历操作数组
    var arr [5]int
    for i := 0; i < len(arr); i++ {
    	arr[i] = i * 2
    }
    for i, v := range arr {
    	fmt.Println("index: ", i, "value: ", v)
    }
    
  • 数组是一种值类型(不像C/C++那样是指向首元素的指针),可以通过new来创建数组,不过它返回的是指向该数组的指针
    d := new([4]int)
    fmt.Println(d)            //&[0,0,0,0]
    fmt.Println(*d)           //[0,0,0,0]
    
    注意区分指向数组的指针和指针数组
    e := [5]int{1, 2, 3, 4, 5}
    var p *[5]int = &e              //p为指向数组e的指针 &[1,2,3,4,5]
    x, y := 1, 2
    z := [2]*int{&x,&y}            //z是一个指针数组
    
  • 多维数组
    h := [2][3]int{
    	{1, 1, 1},
    	{2, 2, 2}
    }
    
  • 数组可以使用==!=来比较
    f := [2]int{1, 2}
    g := [2]int{1, 2}
    fmt.Println(f == g)                 //true
    

切片

    切片(slice)是对某个数组的一段连续片段的引用(该数组我们称为相关数组,该片段可以是整个数组,也可以是数组中的某一段),因此切片是一个引用类型。切片底层实现是数组,它与数组的关系如下图:
Go学习笔记(9)Go容器类型——数组&切片&map_第1张图片
    多个slice可以指向同一个底层相关数组,此时其中一个值改变会影响全部的值

  • 切片是一个可变长的数组,它的长度可以动态修改,可以用len()来获取切片的长度。切片还有另外一个属性容量,表示切片可以达到的最大长度,可以用cap()来获取。切片的容量等于切片的长度+相关数组中在切片之后剩下的的长度。所以切片的长度小于等于切片的容量。
  • 切片的容量是预先分配的,如果在运行的过程中,切片的长度超过了原来分配的容量,则会重新分配一个空间更大的数组,然后将值都拷贝过去
  • 切片的声明格式:var 切片名 []元素类型,这里不需要指定长度,如果是数组则必须指定长度或者用...替代
  • 切片可以通过底层相关数组切取,也可以用make直接创建,还可以通过已有的切片来生成
    • 通过数组生成
      arr := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
      s1 := arr[2:5]           //切取数组arr索引从2~4这一段(即[3,4,5]),注意索引值不包括5
      s2 := arr[:]               //切片为整个arr数组
      s3 := arr[5:]             //切取从索引5开始到末尾,即[6,7,8,9,10]
      
    • 通过make创建
      格式:make([]type, len, cap),其中cap可以省略,省略则默认和len相同
      s4 := make([]int, 3, 20)
      s5 := make([]int, 3)
      
    • 通过切片生成,这种方式有时也被称之未称未切片重组reslice
      sa := []int{1, 2, 3, 4, 5}
      sb := sa[1:4]                    //[2,3,4],len:3,cap:4
      sc := sb[1:4]                    //[3,4,5],len:3,cap:3
      
          需要注意的是,新的切片的索引不能超过原切片的容量,否则会引发编译错误,而不是重新分配数组
  • 在切片上追加元素(append
        如果要在切片上添加新的元素,可以使用append函数,用法为append(被追加元素的slice, 追加的元素...)
    s1 := make([]int, 3, 6)
    s1 = append(s1, 1, 2, 3)     //[0,0,0,1,2,3],还是返回原来的slice (没有超过原来cap)
    s1 = append(s1, 1, 2, 3)     //[0,0,0,1,2,3,1,2,3],返回的是一个新的slice(已经超过了cap,重新分配底层数组)
    
        注意,如果追加后的长度未超过原slice的容量,则返回原始的slice,如果超过了,则重新分配空间更大的数组并拷贝原始数据
  • 拷贝slice
        可以使用copy函数将一个切片拷贝到另外一个切片中,用法copy(目标slice, 被拷贝的slice)
    sc1 := []int{1, 2, 3, 4, 5}
    sc2 := []int{6, 7, 8}
    copy(sc1, sc2)
    fmt.Println(sc1)                 //[6 7 8 4 5]
    copy(sc2,sc1)
    fmt.Println(sc2)                //[6,7,8]
    copy(sc2[0:2], sc1[3:5])       //指定具体位置
    fmt.Println(sc2)               //[4 5 8]
    

Map

    Map是Go里面的键值对集合,由key-value对组成,给定key,可以快速定位到对应的value。也被称为字典、哈希表等

  • map中的key可以是任意能够用==或者!=操作符比较的类型,比如stringintfloat等,不能是函数、map、切片;value可以是任意类型
  • map的声明方式var map变量名 map[key类型]vlaue类型
    var m map[int]string               //声明
    m = map[int]string{1:"a", 2:"b"}  //初始化
    
        特别注意,map必须要初始化才能使用,即如果用上面这种方式,必须要有初始化的语句map1 = map[keyType]ValueType{},否则将报错。当然如果觉得太繁杂,可以使用下面的make语句来替代,更加简洁(主要是因为使用前需要先分配好内存空间给map,使用初始化语句或者make语句才能实现空间的分配)
  • map也属于引用类型,声明的时候不需要知道长度,可以动态增加,可以使用make来创建
    var m1 map[int]string = make(map[int]string)
    m2 := make(map[int]string)           //简要写法
    m1[1] = "ok"                         //插入 (1 : ok)的key-value
    m2[1] = "good"
    
        make创建还可以指定容量,make(map[key类型]value类型,cap)cap为容量,可以省略。超出容量会自动扩容,但为了性能还是尽量提供一个大概的初始值。
  • 获取指定key的value a:=m[1] (获取key为1的value)
  • 插入一个k-v对(或对指定key进行修改)m[1]="ok"
  • 判断键值对是否存在
        上面获取指定key的值map[key]还有另外一种用法:value,isPresent = map[key],即返回两个值,第二个值isPresent是布尔类型,如果该key存在,该值为true,且value为该key对应的值;如果key不存在,则isPresentfalse,且value为空值。
    if value, ok := map1[key1];ok{
    	fmt.Println(value)
    }
    
  • 删除指定key的键值对delete(map1, key1)
  • 使用for-range对map进行遍历
    map1 := make(map[int]string)
    map1[1] = "a"
    map1[2] = "b"
    map1[3] = "c"
    for key, value := range map1 {
    	fmt.Println(key,value)
    }
    
        如果指向获取key或者value,可以这么使用
    for key := range map1 {
    	fmt.Println(key)
    }
    for _, vlaue := range map1 {
    	fmt.Println(value)
    }
    
        需要注意的是,for中获得的key和value值都是副本,直接对这两个值进行修改并不会对原来的map有影响。需要用map[key]才能真正改变map中的值
  • map类型的切片:注意需要使用两次make,第一次分配切片,第二次分配切片中每个map元素
    sm := make([]map[int]string, 5)
    for i := range sm {
    	sm[i] = make(map[int]string,1)
    	sm[i][1]="ok"
    }
    
  • 嵌套map的使用:即map中value也是map类型,这种情况同样需要注意每一个嵌套的map也需要进行make之后才能使用(初始化分配空间)
    var m2 map[int]map[int]string
    m2 = make(map[int]map[int]string)
    m2[1] = make(map[int]string)     //value中嵌套的map也需要进行初始化
    m2[1][1] = "ok1"
    

你可能感兴趣的:(Golang学习笔记,golang,go,编程语言,后端,数据结构)