首页 > 其他分享 >03-Go数组、切片、可变长参数、maps

03-Go数组、切片、可变长参数、maps

时间:2023-02-21 22:26:07浏览次数:43  
标签:03 maps int fmt var 切片 Println map Go

1 数组

# 1 数组是类似于数字、字符串、 的基础数据类型


# 2 数组是同一类型元素的集合
   eg:整数集合:5,8,9,79,76 形成一个数组

  Go--数组      : 不允许混合不同类型的元素   eg: 包含字符串和整数的数组
  python--列表  : 可以放多个任意类型的元素


# 3 数组是连续存储的内存空间,一旦定义,不能扩大或缩小


# 注:
  1.python中没有数组,元组 与 数组 不是同一个东西
package main

import "fmt"

//数组
func main() {
    //1 定义数组,不赋初值   数组元素的值为零值
    var a [4]int8   //定义了一个大小为4的int类型 a 数组,最多放4个值,并且必须都是int类型
    fmt.Println(a)  //[0 0 0 0]
    
    var b [3]string
    fmt.Println(b)

    
    //2 赋值和取值   从0开始 没有负数 没有步长
    var a [4]int8
    a[0] = 99
    
    fmt.Println(a[0])  //99
    fmt.Println(a)     //[99 0 0 0]

    
    //3 定义并赋初值
    //var a [4]int8 = [4]int8{1, 2, 3, 4}
    var a = [4]int8{1, 2, 3, 4}  // 数组定义,必须固定大小
    
    a := [4]int8{1, 2, 3, 4}
    a := [...]int8{1, 2, 3, 4}  //能看懂即可,一般不用
                                //看到...写法,但是实际大小也是固定的,就是定义赋值时的大小
    fmt.Println(a)
    
    
    a :=[100]int8{98:99}  //只把第99位设置为99,其他为零值
    fmt.Println(a[98])


    //4 数组大小也是类型的一部分
    var a = [4]int8{1, 2, 3, 4}
    var b = [3]int8{1, 2, 3}

    // a和b就不是同一个类型
    test1(b)  // test1函数 就不能传递 a,b 两个变量进去,因为类型不一样

    
    
    //5 数组是值类型,不是引用类型  
       当参数传递到函数中,传递的是复制的值,而不是引用地址,不会修改掉原来的数据
       go语言中的函数传递参数,都是copy传递,把参数复制一份传过去
    
    var a = [3]int{2: 66}
    
    test1(a)        // 99,88,66
    fmt.Println(a)  // 0,0,66

    
    //6 数组长度
    var a = [30]int{2: 66}
    fmt.Println(len(a))

    
    
    //7 循环数组   索引循环与迭代循环 
    var a = [3]int{2: 66}

    //7.1 基于索引的循环
    for i := 0; i <len(a); i++ {
        fmt.Println(a[i])
    }

    //7.2 基于迭代的循环 for + range   range后放,key被迭代的东西
      range是关键字,并不是内置函数

    for i := range a{     //如果使用给一个值接收,就是索引
        fmt.Println(v)
    }
    
    //for _, v := range a{  //只需要其中一个,借助'_' 
    //for i, _ := range a{
    
    for i, v := range a{  //如果使用两个值接收,就是索引和值
        fmt.Println(i)
        fmt.Println(v)
    }
    
    
    //8 多维数组
    //长度为3,类型为[4]int的数组   二维数组
    var a [3][4]int
    fmt.Println(a)   //[[0 0 0 0] [0 0 0 0] [0 0 0 0]]


    var a =[3][4]int{{1,1,1,1},{},{5,5,5}}
    fmt.Println(a)  //[[1 1 1 1] [0 0 0 0] [5 5 5 0]]
    
    fmt.Println(a[2][2])  //5
    
    // 循环二维数组 ---> 两层for循环    几维数组,就几层for循环
}

        
func test1(a [3]int)  {
    a[0]=99
    a[1]=88
    fmt.Println(a)  
}

2 切片(slice)

# 切片是由 数组 建立的一种方便、灵活且功能强大的包装(Wrapper)

# 切片本身不拥有任何数据,它们只是对现有数组的引用。


# 注:
  1.非常常用的数据结构
  2.切片是引用类型
package main

import "fmt"

// 切片的使用

func main() {
    //1 切片的定义
    
    // 1.1 方式一:
    //先定义数组
    var a [10]int
    
    //再从数组上切出切片   []后 数组里的类型 
    var s []int=a[:]  //冒号前后写 数组的索引数字   前闭后开
    var s = a[:]      //采用类型推导

    fmt.Println("切片s是:",s)   //ln指的是换行的意思
    fmt.Printf("s的类型是:%T\n",s)  //s的类型是:[]int   --> 切片
    
    fmt.Println("数组a是:",a)
    fmt.Printf("a的类型是:%T\n",a)  //a的类型是:[10]int --> 数组
    
    // 1.2 方式二:定义且赋值
    var s []int=[]int{6, 7, 8}
    var s =[]int{6, 7, 8}   // 类型推导

    
    //2 因为切片的对底层数组的引用,故若改变了切片,底层数组会变  若改变了数组,切片也会变
    fmt.Println("---------------------------")
    s[0]=99       //切片的修改和取值,也是根据索引
    fmt.Println("切片s是:",s)
    fmt.Println("数组a是:",a)
    
    fmt.Println("---------------------------")
    a[1]=88    
    fmt.Println("数组a是:",a)
    fmt.Println("切片s是:",s)


    //3 不从头切到尾,只切一部分  切片和数组都会同时修改
    var a [10]int
    var s []int=a[5:]
    
    fmt.Println(s)  
    
    s[0]=100
    fmt.Println(s)  // [100 0 0 0 0]
    fmt.Println(a)  // [0 0 0 0 0 100 0 0 0 0]
    
    a[9]=999
    fmt.Println(s)  // [100 0 0 0 999]

    
    //4 切片的长度和容量
    var a [10]int
    var s []int=a[5:]
    
    fmt.Println(len(s))  //长度 5
    fmt.Println(cap(s))  //容量(它最大能存多少值)  5

    var a [10]int
    var s []int=a[5:7]   //前闭后开
    
    fmt.Println(s)
    fmt.Println(len(s))  //长度 2
    fmt.Println(cap(s))  //容量(它最大能存多少值)  5    
                         虽然此时长度为2,但原数组切片的后面还有3个
    

    //5 切片的追加值
    var a [10]int
    var s []int=a[5:7]

    //内置函数append
    s=append(s,3)
    fmt.Println(s)       //[0 0 3]
    fmt.Println(len(s))  //长度 3
    fmt.Println(cap(s))  //容量 5
    
    //追加到最大长度
    s=append(s,3,4,5)    //返回值还是切片类型
    fmt.Println(s)       //[0 0 3 4 5]
    fmt.Println(len(s))  //长度 5
    fmt.Println(cap(s))  //容量 5
    fmt.Println(a)       //[0 0 0 0 0 0 0 3 4 5]
    
    //再追加一个
    s=append(s,6)
    fmt.Println(s)       //[0 0 3 4 5 6]
    fmt.Println(len(s))  //长度 6
    fmt.Println(cap(s))  //容量 10
                           如果不够了,翻倍--原来容量的2倍(在1024以内)
    
    fmt.Println(a)       //[0 0 0 0 0 0 0 3 4 5]
    //此时该切片已经脱离原来的数组了,自己新造了一个数组,指向了新数组,并且把值copy了过去
    //现在改数组,不会影响切片;改切片,也不会影响数组
    
    
    a[9]=999
    s[2]=888
    fmt.Println(a)  //[0 0 0 0 0 0 0 3 4 999]
    fmt.Println(s)  //[0 0 888 4 5 6]


    
    //6 只定义切片 不赋值,不知道长度和容量     引用类型的零值都是nil
    var s []int    //零值: nil ---> python的None、java的null
                     
    fmt.Println(s)  //[]
    
    if s== nil{
        fmt.Println("我是空的")
    }else {
        fmt.Println("我不是空的")
    }

    
    //7 通过make内置函数 创建切片
        第一个参数是类型,第二个参数是长度,第三个参数是容量
    
    var s []int= make([]int,3,4)
    //var s []int= make([]int,3)  //第三个参数容量不写,同长度一样
    
    fmt.Println(s)  //[0 0 0]  为何不是[],因为知道长度为3,类型为int 零值就是0

    s[0]=99
    fmt.Println(s)       //[99 0 0]
    fmt.Println(len(s))  //长度  3
    fmt.Println(cap(s))  //容量  4


    //8 切片是引用类型   当做参数传递时,修改值 会不会影响原来的切片?
        注意:前提取决于是否追加 超过到最大容量:
             若没有超过,修改值 --> 会影响原来的数组,从而影响原来的切片
             若超过,   修改值 --> 会创建新的数组,不会影响原来的数组  从而不影响原来的切片
 
    var s []int= make([]int,3,4)
    //go 函数参数传递都是copy传递,而s是引用类型,传递的是新复制出来的一个切片引用
    
    //eg1: 不追加,只修改值    会影响原来的切片
    test1(s)         
    fmt.Println(s)  //[999 0 0]
    
    //eg2: 追加但没超,修改值   会影响原来的切片
    test2(s)        
    fmt.Println(s)  //[999 0 0]
    
    //eg2: 追加且超过,修改值   不会影响原来的切片
    test3(s)         
    fmt.Println(s)  //[0 0 0]
    
    
    //8 切片是引用类型   当做参数传递时,在函数内部追加值 会不会影响?  不会影响原来的切片
        只追加值,根据是否超过容量,有可能会影响原来的数组   但不会影响原来的切片  
        若只是增加了数组,不修改数组值,原来的切片不受影响

    var s []int= make([]int,3,4)
    test4(s)
    fmt.Println(s)  //[999  0  0]


    //9 切片底层存储
    type slice struct {
        Length        int      //长度
        Capacity      int      //容量
        ZerothElement *byte    //指向数组的某个位置
    }

    
    //10 多维切片   二维切片   注:内层切片也要初始化,否则是nil
    
    //10.1 定义
    //初始化了第一层切片
    var s [][]int=make([][]int,3,4)  
    
    fmt.Println(s)          //[[]  []  []]
    fmt.Println(s[0]==nil)  //true   切片的零值为nil
    
    //迭代循环:初始化第二层切片
    for i:=range s{
        s[i]=make([]int,3,4)
    }

    //索引循环:初始化第二层切片
    for i := 0; i < len(s); i++ {
        s[i]=make([]int,3,4)
    }
    
    fmt.Println(s)   //[[0 0 0] [0 0 0] [0 0 0]]

    
    //10.2 定义且赋值
    var s [][]int=[][]int{{1,2,},{3,4,5,6,7,8},{6,7,8,},{8,7,7,7,7}}
    
    fmt.Println(len(s))     //4
    fmt.Println(cap(s))     //4
    fmt.Println(len(s[1]))  //6
    fmt.Println(cap(s[1]))  //6



    //11 内存优化   copy函数
         只要切片在内存中,数组就不能被垃圾回收
        1w个byte--->1024个byte--->1k--->10k
    
    //var a [10000]int8=[10000]int8{0:11,1:22,2:33}
    var a [10000]int8=[10000]int8{11,22,33}
    
    var s1 []int8=a[:3]
    fmt.Println(s1)   //使用s1时,一直使用的a数组,该数组比较大 但切片只用小部分
    
    var s2 []int8=make([]int8,2,2)
    fmt.Println(s2)   //使用s2时,是基于新创建的小数组
    
    copy(s2,s1)       //把s1切片copy给s2切片
                      如果新切片 长于 旧切片  新切片后面用各类型的零值补齐
                      如果新切片 短于 旧切片  以新切片的长度为准
    fmt.Println(s2)
}



func test1(a []int)  {
    a[0]=999
    fmt.Println(a)  //[999 0 0]
}

func test2(a []int)  {
    a=append(a,888)  //追加但没超
    a[0]=999
    fmt.Println(a)  //[999 0 0 888]
}

func test3(a []int)  {
    a=append(a,888,999)  //追加且超过
    a[0]=999
    fmt.Println(a)  //[999 0 0 888 999]
}

func test4(a []int)  {
    a[0]=999
    a=append(a,888,777,666)  //追加超过最大容量,切片引用指向新的数组了
    a[1]=222
    fmt.Println(a)  //[999 222 0 888 777 666]
}

3 可变长参数

//可变长参数 就是切片类型


package main

import "fmt"


func main() {
    test(1,2,3,4,5)

    //可直接传切片,需要加 ...    '...' 类似于把切片中的元素 打散传递进去
    var s []int=[]int{1,2,3,4,5}
    test(s...)
}


func  test(a ...int)  {
    fmt.Println(a)      //a就是一个切片
    fmt.Println(a[0])   //1
    fmt.Printf("%T",a)  //[]int
}

4 字典(maps)

# maps 是在 Go 中将值(value)与键(key)关联的内置类型
  通过相应的键可以获取到值
  其实就key:value这种结构,类似于python字典
package main

import "fmt"


// maps

func main() {
    //1 定义map: map[key类型]value类型
    var m map[int]string    //零值是nil -->引用类型
    
    fmt.Println(m)          //map[]  其实就nil
    fmt.Println(m == nil)   //true

    
    //2 定义并初始化
    //var m map[int]string=make(map[int]string,3)  //不要传数字,数字:底层数组的长度
    
    var m map[int]string=make(map[int]string)
    
    fmt.Println(m)         //是空map  map[]
    fmt.Println(m==nil)    //false

    
    //3 定义并初始化,放入值  
    var m map[int]string=map[int]string{1:"lqz",2:"xxx",9:""}
    
    
    //4 赋值、修改值
    //var m map[int]string   //如果不初始化,nil赋值会报错
    var m map[int]string=make(map[int]string)  // 初始化了不报错
    
    m[1]="lqz"    //如果没有初始化,赋值报错
    m[1]="egon"
    m[2]="xxx"    //如果存在就修改,不存在,就新增

    fmt.Println(m)  //map[1:egon 2:xxx]

    
    //5 取值
    var m map[int]string=make(map[int]string)

    m[1]="egon"
    m[2]="xxx"
    
    fmt.Println(m[1])   //egon
    fmt.Println(m[9])   //不报错,返回value值的零值

    
    //6 判断一个key是否在map中
    var m map[int]string=map[int]string{1:"lqz",2:"xxx"}  //定义并初始化,放入值

    v,ok :=m[2]   //取值可以返回两个
                   如果2个变量来接收,值 和 布尔(存在就是true,不存在就是false)
                   如果1个变量来接收,值
    
    fmt.Println(v)   //xxx
    fmt.Println(ok)  //true
    
    
    var m map[int]string=map[int]string{1:"lqz",2:"xxx"}
    
    if _,ok:=m[9];ok{
        fmt.Println("值在")
    }else {
        fmt.Println("值不在")
    }

    
    //7 删除元素
    var m map[int]string=map[int]string{1:"lqz",2:"xxx"}
    
    delete(m,1)
    fmt.Println(m)  //map[2:xxx]

    
    //8 长度
    //var m map[int]string=map[int]string{1:"lqz",2:"xxx"}   //肉眼可见 长度为2
    var m map[int]string=make(map[int]string,5)   //数字: 底层数组的长度      一般不写
                                                  不写,默认会有一个固定长度
                                                  若你能预计map的长度,可以写上
                                                  但若是超过该长度,内部就会存在数组扩容  反而导致资源浪费
    
    fmt.Println(len(m))
    fmt.Println(cap(m))  //报错,map没有容量的概念

    
    //9 Map 是引用类型
    //var m map[string]string=map[string]string{1:"lqz",2:"xxx"}
    m :=map[string]string{"name":"lqz","age":"18"}
    
    test(m)
    fmt.Println(m)  //map[name:egon, age:18]


    //10 Map 的相等性    
       map之间不能使用 '==' 操作符判断, '=='只能用来检查map是否为 nil
    
    map1 := map[string]int{"one": 1, "two": 2}
    
    map2 := map1
    
    if map1 == map2 {  //报错
    }

    
    //11 循环
    m :=map[string]string{"name":"lqz","age":"18"}
    
    for k,v:=range m{
        fmt.Println(k)
        fmt.Println(v)
    }

    
    //12 map的存储是无序的 ---> hash类型本来就是无序的 
         python3.6 将字典以后做成有序了,本质是内部加了一个列表,来表示顺序位置

    
    
    //13 map底层存储,都是以数组形式存储
       map、字典 ---> 统统称为hash类型,对key值执行hash
       
    //var m map[string]string
    1.新定义map时,底层会创建出 默认长度(或指定数字的那个长度)的数组
    
    //m["name"] = "lqz"
    2.在对map赋值时
       ---> 会将map的key值,进行hash,得到一个数字
       ---> 再将该数字,对数组长度 取余
       ---> 余数 一定会落在数组的长度范围内
       ---> 最后,在数组[余数的位置上],存储value值
    
    3.在对map取值时   //时间复杂度 O(1)   一次就能命中
       ---> 会将map的key值,进行hash,得到一个数字
       ---> 再将该数字,对数组长度 取余
       ---> 余数 一定会落在数组的长度范围内
       ---> 最后,在数组[余数的位置上],获取value值
    
    4.可能造成hash冲突  //在更多的值进行存储时,hash取余后,余数可能相同,进行存放时 就会冲突
    
    
    //14 解决hash冲突的方法    
         下面方法在数据够多时,会额外增加查找的时间复杂度
         故语言内部都会有一个监测因子,达到一定时,会自动扩容底层数组
    
    1.再哈希法   //最简单
        采用新的hash方法函数,再hash一次,得到一个新地址放进去
    
    
    2.链地址法   //Java hashmap就是这么做的
        如果冲突了,直接使用 链表 直接挂载当前位置下面  
          --->为减少时间复杂度,又使用 平衡二叉树、红黑树 进行挂载 
    
    3.开放定址法  //python 字典使用这种
      在产生冲突时,就直接向后找一个空的单元来存放关键字
        -线性探测再散列
        -二次探测再散列
        -伪随机探测再散列

    4.建立一个公共溢出区  
        当溢出发生时,将所有溢出数据统一放到溢出区

}


func test(m map[string]string)  {
    m["name"]="egon"
    fmt.Println(m)  //map[name:egon, age:18]
}

标签:03,maps,int,fmt,var,切片,Println,map,Go
From: https://www.cnblogs.com/Edmondhui/p/17142680.html

相关文章