美文网首页
Go语言基础

Go语言基础

作者: 闫希鹏 | 来源:发表于2021-08-09 19:44 被阅读0次

    使用Go编写第一行代码

    新建golangFile文件夹,在文件夹中新建main.go,在里面写入如下代码

    package main
    
    import "fmt"
    
    func main() {
        fmt.Println("hellow world")
    }
    

    编译

    go build main.go

    或者直接编译当前包含go代码的文件夹

    go build

    会看到编译后文件夹内多出

    main.exe或者golangFile.exe

    直接在终端执行就会看到“hello world”了

    .\main.exe

    如果想查看编译过程

    go build -x main.go

    如果想指定一个编译后的二进制文件名称

    go build -x -o hello.exe main.go

    如果省去手动编译过程,一步编译+执行

    go run main.go 这样直接在一个其它临时的目录生成.exe并执行出结果


    定义变量

    *匿名变量 _ :是一个go已定义的变量,本身这种变量不会进行空间分配,也不会占用一个变量的名字。
    相当于一个黑洞,对它的任何赋值都会被吃掉不保存,通常来用作占位符

    package main
    
    import "fmt"
    
    //定义包级别的变量
    var (
        name string = "yxp"
        age  int    = 29
    )
    
    //定义变量可以省略类型,编译器根据字面量去猜这个变量的类型
    var (
        name2 = "zxm"
        age2  = 28
    )
    
    //不推荐这种在一行里定义多个变量
    var name3, age3 = "hhh", 29
    
    func main() {
        //定义函数内部变量
        var test string
        //变量的初始化
        test = "asd"
        fmt.Println(test)
        fmt.Println(name)
        //这是一个简短声明,简短声明只能在函数内部
        yyds := true
        //简短声明多个变量,其中的变量至少有一个是未定义的
        _, num2 := 1, 2
        fmt.Println(yyds)
        //赋值
        test, name = "this is test", "this is name"
        fmt.Println(test, name)
        //小tips,交换test和name的值
        test, name = name, test
        fmt.Println(test, name)
    }
    

    常量

    package main
    
    import "fmt"
    
    func main() {
        const NAME string = "yxp"
        //可以省略类型
        const AGE = 18
        //定义多个常量
        const (
            AGE1  int    = 19
            NAME1 string = "yyy"
        )
        //可以省略类型
        const (
            AGE2  = "yyds"
            NAME2 = 20
        )
        //也可以这样
        const AGE3, NAME3 = 21, "jkl"
        //同类型的常量第一个赋值之后,后面的常量值和第一个值相等
        const (
            AGE4 int = 1
            AGE5
            AGE6
        )
        fmt.Println(AGE4, AGE5, AGE6)
        //也可以这样写
        const (
            A1 int = 1
            A2
            A3
            A4 float64 = 0.1
            A5
            A6
        )
        fmt.Println(A1, A2, A3, A4, A5, A6)
        //枚举,const_iota
        const (
            B1 int = iota //0
            B2            //1
            B3            //2
        )
        fmt.Println(B1, B2, B3)
        const (
            C1 int = (iota + 1) * 100 //100
            C2                        //200
            C3                        //300
        )
        fmt.Println(C1, C2, C3)
    }
    
    

    作用域、块

    package main
    
    import "fmt"
    
    func main() {
        //作用域、块。由{}来划分
        outer := "outer"
        outDeclare := "outDeclare"
        {
            inner := "inner"
            //里面可以访问外面的
            fmt.Println(outer)
            fmt.Println(inner)
            //在里面改变外面的
            outer = "ch_outer"
            fmt.Println(outer)
            //块内重新定义块外的变量
            outDeclare := "bloack_outDeclare"
            fmt.Println(outDeclare)
        }
        fmt.Println(outer)
        fmt.Println(outDeclare)
        //外面不能访问里面的
        //fmt.Println(inner)
    }
    
    

    布尔类型变量

    package main
    
    import "fmt"
    
    func main() {
        //不初始化默认是false
        var zero bool
        //布尔类型只有两个值true、false。不会像别的语言可以是0和1
        //布尔类型占用空间1字节
        isBoy := true
        isGirl := false
        fmt.Println(zero, isBoy, isGirl)
    }
    

    int类型变量

    package main
    
    import "fmt"
    
    func main() {
    
        var age int = 31
        fmt.Printf("%T %d\n", age, age)
        //打印查看八进制和十六进制
        fmt.Println(0777, 0x10)
        //位运算
        //十进制转换二进制 辗转相除法(请百度)
        //十进制7转换成二进制
        //7/2=3...1, 3/2=1...1, 1/2=0...1
        //0111
        //十进制2转二进制
        //2/2=1...0
        //10
        //7 & 2 => 0111 & 0010 => 0010 就是十进制的2
        //7 | 2 => 0111 | 0010 => 0111 就是十进制的7
        //7 ^ 2 => 0111 ^ 0010 => 0101 就是十进制的5
        //2 << 1 => 0010 << 1  => 0100 就是十进制的4
        //2 >> 1 => 0010 >> 1  => 0001 就是十进制的1
        //7 &^ 2 => 0111 &^ 0010 => 0101就是十进制的5;
        //&^ 按位清空,就是两个数字按位一一对应,第二个数字为1的位置,第一个数字就要为0,结果取第一个数字改变后的样子
        fmt.Println(7 & 2)
        fmt.Println(7 | 2)
        fmt.Println(7 ^ 2)
        fmt.Println(2 << 1)
        fmt.Println(2 >> 1)
        fmt.Println(7 &^ 2)
    
        //int/uint/byte/rune/int*
        var intA int = 10
        var uintB uint = 3
        fmt.Println(intA + int(uintB)) //两种类型的数需要强制转换成一种才能计算
        fmt.Println(uint(intA) + uintB)
    
        //强制类型转换要注意会溢出丢失,从大往小转
        var intC int = 0xfffff
        fmt.Println(intC, uint8(intC))
    
        //byte,rune。实际上也是int(百度搜索这两种类型,理解的不太清楚)
        
        var a byte = 'A'
        var w rune = '中'
        fmt.Println(a, w)
        fmt.Printf("%T %d %b %o %x\n", age, age, age, age, age)
        fmt.Printf("%T %c\n", a, a)
        fmt.Printf("%T %q %U %c\n", w, w, w, w)
        age = 1234
        fmt.Printf("%5d\n", age)  //一共占5位,不够在前面补空格,超出不处理
        fmt.Printf("%05d\n", age) //一共占5位,不够在前面补0
        fmt.Printf("%-5d\n", age) //左对齐,在右边补位。默认是右对齐,在左边补位
    }
    

    byte和rune详解

    他们都属于别名类型,byte是uint8(无符号8位整数)的别名类型,rune是int32(有符号32位整数)的别名类型

    package main
    
    import ( 
        "fmt" 
    )
    
    func main() {
        //rune类型可以表示一个Unicode字符,rune类型的值需要用''包裹
        //直接使用Unicode支持的字符赋值,这种表示方法最通用,其他的的了解就好
        var char rune = '赞' 
        //一个Unicode代码点通常由“U+”和一个以十六进制表示法表示的整数表示。例如,英文字母“A”的Unicode代码点为“U+0041”。
        fmt.Printf("字符 '%c' 的Unicode代码点是 %s。\n", char1, ("U+8D5E"))
        
        var ch1 rune = '\101'//表示字母A,"\"加三位八进制数,只能表示值在[1,255]内的字符
        var ch2 rune = '\x41'//表示字母A,"\x"加两个十六进制数,只能表示ASCLL支持的字符
        var ch3 rune = '\u90DD'//表示“郝”,'\u'加四位十六进制数,只能表示编码值在[0,65535]内的字符
        var ch4 rune = '\U000090DD'//表示“郝”,'\U'加四位十六进制数,可表示任何Unicode字符
        fmt.Println(char,ch1,ch2,ch3,ch4)
        fmt.Printf("char:%T %c\n", char, char);
        fmt.Printf("ch1:%T %c\n", ch1, ch1);
        fmt.Printf("ch2:%T %c\n", ch2, ch2);
        fmt.Printf("ch3:%T %c\n", ch3, ch3);
        fmt.Printf("ch4:%T %c\n", ch4, ch4);
        //rune类型值的表示中支持转义字符
        var ch5 rune = '\''//单引号,Unicode代码点U+0027
        var ch6 rune = '"'//双引号,Unicode代码点U+0022
        var ch7 rune = '\\'//反斜杠,Unicode代码点U+005c
        var ch8 rune = '\r'//回车,Unicode代码点U+000D
        var ch9 rune = '\t'//水平制表符,Unicode代码点U+0009
        var ch10 rune = '\n'//换行符,Unicode代码点U+000A
        var ch11 rune = '\b'//退格符,Unicode代码点U+0008
        var ch12 rune = '\a'//告警铃声或蜂鸣声,Unicode代码点U+0007
        fmt.Printf("ch5:%T %c\n", ch5, ch5);
        fmt.Printf("ch6:%T %c\n", ch6, ch6);
        fmt.Printf("ch7:%T %c\n", ch7, ch7);
        fmt.Printf("ch8:%T %c\n", ch8, ch8);
        fmt.Printf("ch9:%T %c\n", ch9, ch9);
        fmt.Printf("ch10:%T %c\n", ch10, ch10);
        fmt.Printf("ch11:%T %c\n", ch11, ch11);
        fmt.Printf("ch12:%T %c\n", ch12, ch12);
    
    }
    
    
    

    浮点类型变量

    package main
    
    import "fmt"
    
    func main() {
        //float32,float64只有这两种类型
        var high float64
        fmt.Printf("%T %f\n", high, high)
        fmt.Println(1.1 + 1.2)
        fmt.Println(1.1 - 1.2)
        fmt.Println(1.1 * 1.2)
        fmt.Println(1.1 / 1.2) //0.9166666666666666
        high = 1.64
        high++
        //浮点数计算是有精度损耗的,不准确。结果是2.6399999999999997
        fmt.Println(high)
        fmt.Printf("%T %5.2f\n", high, high) //保留5位2个小数点
        //关系运算
        //浮点数一般不计算== !=,因为精度损耗结果可能不准确
        //一般计算 > >= < <=
    
        //如果要计算== != 请百度搜搜
    
    }
    
    

    字符串类型变量(含切片和索引)

    package main
    
    import "fmt"
    
    func main() {
        //特殊字符 \n \f \t \r...在""里可以被转义,在``里不会被转义
        var name string = "as\tdasa" //可解释字符串
        var desc string = `aaa\taa`  //原生字符串
        fmt.Println(name, desc)
        //字符串链接
        fmt.Println("我叫" + "hhh")
        //fmt.Println("我叫" + 'Q')//报错 字符串和字符不能连接
        //字符串之间的比较
        fmt.Println("a" == "b")
        fmt.Println("a" > "b")
        fmt.Println("a" >= "b")
        fmt.Println("a" < "b")
        fmt.Println("a" <= "b")
        fmt.Println("a" != "b")
        //使用索引或者切片操作,字符串定义的内容只能为ASCII
        desc = "abcdefg"
        //索引为从0~(n-1)
        fmt.Printf("%T %c\n", desc[0], desc[0]) //a
        //切片[strat:end],取出从[star ~ (end-1)]之内的字符
        fmt.Printf("%T %s\n", desc[0:3], desc[0:3]) //abc
        //如果不是ASCII
        desc = "哈哈哈"
        fmt.Printf("%T %c\n", desc[0], desc[0])     //乱码
        fmt.Printf("%T %s\n", desc[0:2], desc[0:2]) //乱码
        //得到字符串长度,也要是ASCII字符串,实际上len()得到的是字符串所占字节
        desc = "abc"
        fmt.Println(len(desc)) //3
        desc = "几十块"
        fmt.Println(len(desc)) //9
    }
    
    

    指针类型变量

    package main
    
    import "fmt"
    
    func main() {
        a := 2
        var b int
        fmt.Println(a, b) //2 0
        b = 3
        fmt.Println(a, b) //2 3
        //指针
        var c *int
        c = &a
        //c := &a
        fmt.Printf("%T %d\n", c, c)
        fmt.Printf("%T %d\n", *c, *c)
        //通过指针修改变量a的值
        *c = 4
        fmt.Println(a, *c)
    }
    
    

    scan方法,用户输入方法

    package main
    
    import "fmt"
    
    //用户输入
    func main() {
        var name string
        fmt.Println("请输入名字")
        fmt.Scan(&name)
        fmt.Println("您输入的内容是:" + name)
    
        //输入内容要保证是int,不是int会输出0
        var age int
        fmt.Println("请输入年龄")
        fmt.Scan(&age)
        fmt.Println("年龄是:", age)
    
        //如果上一个输入的类型错误,那么这里也会使用上一个错的数据类型自动输入
        var height string
        fmt.Println("请输入身高")
        fmt.Scan(&height)
        fmt.Println("身高是:", height)
    }
    
    

    if 语句

    语法

    if 布尔表达式 {
        /* 在布尔表达式为 true 时执行 */
    }
    
    func main() {
        x := 0
    
        if n := "abc"; x > 0 {//定义了局部变量n,只能在块内访问
            fmt.Printf("%T : %c", n[2], n[2])
        } else if x < 0 { // 注意 else if 和 else 左大括号位置。
            fmt.Printf("%T : %c", n[1], n[1])
        } else {
            fmt.Printf("%T : %c", n[0], n[0])
        }
    }
    

    不支持三元操作符(三目运算符) "a > b ? a : b"。


    switch语句

    Golang switch 分支表达式可以是任意类型,不限于常量。可省略 break,默认自动终止。

    switch var1 {
        case val1:
            ...
        case val2:
            ...
        default:
            ...
    }
    

    变量 var1 可以是任何类型,而 val1 和 val2 则可以是<u>同类型的</u>任意值。类型不被局限于常量或整数,但<u>必须是相同的类型;或者最终结果为相同类型的表达式</u>。 您可以同时测试多个可能符合条件的值,使用逗号分割它们,例如:case val1, val2, val3。

    var grade string = "B"
        var marks int = 90
    
        switch marks {
        case 90:
            grade = "A"
        case 80:
            grade = "B"
        case 50, 60, 70:
            grade = "C"
        default:
            grade = "D"
        }
        //省略条件表达式,可当 if...else if...else
        switch {
        case grade >= "A":
            fmt.Printf("优秀\n")
        case grade >= "B":
            fmt.Printf("良好\n")
        case grade >= "D":
            fmt.Printf("及格\n")
        case grade <= "D":
            fmt.Printf("不及格\n")
        }
    
        //fallthrough
        var k = 0
        switch k {
        case 0:
            println("fallthrough")
            fallthrough
            /*
               Go的switch非常灵活,表达式不必是常量或整数,执行的过程从上至下,直到找到匹配项;
               而如果switch没有表达式,它会匹配true。
               Go里面switch默认相当于每个case最后带有break,
               匹配成功后不会自动向下执行其他case,而是跳出整个switch,
               但是可以使用fallthrough强制执行后面的case代码。
            */
        case 1:
            fmt.Println("1")
        case 2:
            fmt.Println("2")
        default:
            fmt.Println("def")
        }
    

    Type Switch

    switch 语句还可以被用于 type-switch 来判断某个 interface 变量中实际存储的变量类型。

    格式:

    switch x.(type){
        case type:
           statement(s)      
        case type:
           statement(s)
        /* 你可以定义任意个数的case */
        default: /* 可选 */
           statement(s)
    }
    

    实例:

    var x interface{}
    
    switch i := x.(type) { // 带初始化语句
        case nil:
            fmt.Printf(" x 的类型 :%T\r\n", i)
        case int:
            fmt.Printf("x 是 int 型")
        case float64:
            fmt.Printf("x 是 float64 型")
        case func(int) float64:
            fmt.Printf("x 是 func(int) 型")
        case bool, string:
            fmt.Printf("x 是 bool 或 string 型")
        default:
            fmt.Printf("未知型")
    }
    

    for循环结构

    
    # for循环结构
    ```go
    package main
    
    func main() {
        s := "abc"
    
        // 常见的 for 循环,支持初始化语句。
        for i, n := 0, len(s); i < n; i++ { 
        println(s[i])
        }
    
        // 替代 while (n > 0) {}
        n := len(s)
        for n > 0 { 
        println(s[n-1]) // 替代 for (; n > 0;) {}
        n--
        }
        // 替代 while (true) {}
        // 替代 for (;;) {}
        for { 
        println(1) 
        }
    }
    
    

    for range

    s := "abc"
    //忽略 value,支持 string/array/slice/map。
    for i := range s {
        println(s[i])
    }
    //忽略 key。
    for _, c := range s {
        println(c)
    }
    // 忽略全部返回值,仅迭代。
    for range s {
    
    }
    
    m := map[string]int{"a": 1, "b": 2}
    // 返回 (key, value)。
    for k, v := range m {
        println(k, v)
    }
    

    9*9乘法表

    package main
    
    import "fmt"
    
    func main() {
        var a int = 0
        var b int = 0
        for a = 1; a < 10; a++ {
            for b = 1; b < 10; b++ {
                var res int
                if b <= a {
                    res = a * b
                    fmt.Printf("%d*%d=%d ", b, a, res)
                }
    
            }
            fmt.Printf("\n")
        }
    }
    
    1*1=1 
    1*2=2 2*2=4
    1*3=3 2*3=6 3*3=9
    1*4=4 2*4=8 3*4=12 4*4=16
    1*5=5 2*5=10 3*5=15 4*5=20 5*5=25
    1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36
    1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49
    1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64
    1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81
    

    数组

    package main
    
    import "fmt"
    
    func main() {
        //定义数组
        var num [10]int
        var boolType [10]bool
        var strType [10]string
        //查看类型和每个数组单元默认值是什么
        fmt.Printf("%T   %d\n", num, num)
        fmt.Printf("%T   %t\n", boolType, boolType)
        fmt.Printf("%T   %s\n", strType, strType)
        /*数组字面量的三种表示方法*/
        //只定义前几个数组元素
        num = [10]int{1, 2, 3}
        fmt.Println(num)
        //指定数组某个单元的值
        num = [10]int{1: 10, 8: 80, 9: 90}
        fmt.Println(num)
        //不声明数组的长度,数组字面量的元素个数要和数组变量num能容纳的元素个数一致
        num = [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
        fmt.Println(num)
    
        /*数组的计算*/
        arr1 := [...]int{1, 2, 3}
        arr2 := [...]int{1, 2, 2}
        fmt.Println(arr1 == arr2)
        //len()获得数组的长度
        arr3 := [...]string{"asd", "aaa"}
        fmt.Println(len(arr3))
        //索引
        arr3[1] = "hhahah"
        fmt.Println(arr3[0])
    
        //字符串切片
        var s string = "qwertyuiop"
        fmt.Printf("%T %v\n", s[0:4], s[0:4])
        //数组切片
        var arr4 [10]int
        arr4 = [10]int{1, 2, 3, 4}
        fmt.Printf("%T %v\n", arr4[0:4], arr4[0:4])
        //多维数组
        var arr5 [3][2]int
        fmt.Printf("%T %v\n", arr5, arr5)
        arr5 = [3][2]int{{1}, {2, 2}}
        fmt.Printf("%T %v\n", arr5, arr5)
    }
    
    

    切片slice

    需要说明,slice 并不是数组或数组指针。它通过内部指针和相关属性引用数组片段,以实现变长方案。

    ------------有时间详细了解切片到底是不是指针

    nil 简单来说:

    nil是一个预先声明的标识符,表示指针、通道、函数、接口、映射或切片类型。
    nil可以代表很多类型的零值

    ------------有时间详细总结下nil

    //定义一个切片
    var num []int
    fmt.Printf("%T len:%d cap:%d\n", num, len(num), cap(num))
    if num == nil {
        fmt.Println("未初始化的切片num 等于 nul,其实切片底层就是一个指针,指针的默认值是空,所以这里切片默认值就是空")
    }
    //切片字面量,和初始化
    num = []int{1, 2, 3}
    fmt.Printf("初始化切片:%#v len:%d cap:%d\n", num, len(num), cap(num))
    //切片可长度可变
    num = []int{1, 2, 3, 4}
    fmt.Printf("改变了切片的长度:%#v len:%d cap:%d\n", num, len(num), cap(num))
    //数组长度不可变,以下代码会报错
    //var arr = [3]int{1, 2, 3}
    //arr = [2]int{1, 2}
    
    //数组切片赋值给切片
    var arr1 [10]int = [10]int{1, 2, 3, 4, 5}
    num = arr1[0:8]
    fmt.Printf("通过数组得到的切片:%#v len:%d cap:%d\n", num, len(num), cap(num))
    //make函数
    num = make([]int, 3)
    fmt.Printf("通过make函数只声明len的切片:%#v len:%d cap:%d \n", num, len(num), cap(num))
    num = make([]int, 3, 5)
    fmt.Printf("通过make函数声明了len和cap的切片:%#v len:%d cap:%d \n", num, len(num), cap(num))
    /*
        切片为什么要有容量的概念?为什么不直接使用长度len
        因为切片是可变长的,每次变长都会重新生在内存开辟一片更大的空间,然后把这个空间的地址重新赋值给变量
        那么如果每增加一个元素就新开辟空间再赋值有点浪费计算机资源
        所以可以生成冗余的空间,我们当前用到几个单元就是len,如果需要增加元素就从cap中再给一个len就ok
    */
    
    /*切片的操作,增、删、改、查*/
    //查
    fmt.Printf("根据索引查看切片元素:%d\n", num[0])
    //fmt.Println(num[4]) //无法查到4,index out of range [4] with length 3
    //改
    num[0] = 10
    fmt.Printf("查看通过索引改变了的切片元素:%d\n", num[0])
    //增
    num = append(num, 333)
    fmt.Printf("查看append之后的切片:%#v len:%d cap:%d \n", num, len(num), cap(num))
    //超出切片容量之后,会自动扩展容量。底层其实是重新申请一个更大的内存空间,并把空间的地址赋值给切片变量
    num = append(num, 444, 555)
    fmt.Printf("查看append了超出原有容量的切片:%#v len:%d cap:%d \n", num, len(num), cap(num))
    
    //切片的切片
    newNum := num[1:3]
    //new_cap = 10-1
    fmt.Printf("查看切片的切片:%#v new_len:%d new_cap:%d \n", newNum, len(newNum), cap(newNum))
    newNum = num[1:3:6]
    //new_cap = 6-1
    fmt.Printf("查看切片的切片:%#v new_len:%d new_cap:%d \n", newNum, len(newNum), cap(newNum))
    //切片的切片容量不能设置的比原来的大
    //n := num[1:3:100]//这是会报错的
    
    //遍历切片
    for k, v := range num {
        fmt.Println(k, v)
    }
    
    /*
        切片的副作用
        切片其实是使用源的地址。而不是把源的值复制一份使用
    */
    //切片的切片被改
    num = make([]int, 3, 5)
    num01 := num[1:3]
    fmt.Println(num, num01)
    num01[0] = 1
    fmt.Println(num, num01)
    //增加num01的元素
    num01 = append(num01, 111)
    fmt.Println(num, num01)
    //增加num的元素
    num = append(num, 222)
    fmt.Println(num, num01)
    //直到num或num1之中的哪一个先append超出了cap容量,那么就会重新分配空间,两者才会分开
    num01 = append(num01, 333, 444)
    fmt.Println(num, num01)
    //数组的切片被改
    var arr2 = [5]int{0, 1, 2, 3, 4}
    var num02 = arr2[:] //只写一个[:]代表生成一个和原数组一样的切片
    fmt.Println(arr2, num02)
    num02[0] = 111
    fmt.Println(arr2, num02)
    
    //删-copy。在go中常用copy来实现删除切片元素
    sArr := []int{1, 2, 3}
    sArr1 := []int{11, 22, 33, 44}
    copy(sArr, sArr1) //把sArr1 copy到sArr上
    fmt.Printf("使用copy的效果:%v\n", sArr)
    //删除切片第一个元素和最后一个元素
    sArr2 := []int{1, 2, 3, 4, 5}
    fmt.Printf("删除切片第一个元素:%v\n", sArr2[1:])
    fmt.Printf("删除切片最后一个元素:%v\n", sArr2[0:len(sArr2)-1])
    //删除切片的中间元素
    copy(sArr2[2:], sArr2[3:])
    sArr2 = sArr2[0 : len(sArr2)-1]
    fmt.Printf("删除sArr2[2]元素:%v\n", sArr2)
    
    /*利用append和删除首尾元素的方法实现,堆栈和队列*/
    //队列-先进先出
    queue := []int{}
    fmt.Println(queue)
    queue = append(queue, 1)
    fmt.Println(queue)
    queue = append(queue, 2)
    fmt.Println(queue)
    queue = append(queue, 3)
    fmt.Println(queue)
    queue = queue[1:]
    fmt.Println(queue)
    queue = queue[1:]
    fmt.Println(queue)
    queue = queue[1:]
    fmt.Println(queue)
    //堆栈-先进后出
    stack := []int{}
    fmt.Println(stack)
    stack = append(stack, 1)
    fmt.Println(stack)
    stack = append(stack, 2)
    fmt.Println(stack)
    stack = append(stack, 3)
    fmt.Println(stack)
    stack = stack[0 : len(stack)-1]
    fmt.Println(stack)
    stack = stack[0 : len(stack)-1]
    fmt.Println(stack)
    stack = stack[0 : len(stack)-1]
    fmt.Println(stack)
    

    总结:

    1. 切片:切片是数组的一个引用,因此切片是引用类型。但自身是结构体,值拷贝传递。
    
    2. 切片的长度可以改变,因此,切片是一个可变的数组。
    
    3. 切片遍历方式和数组一样,可以用len()求长度。表示可用元素数量,读写操作不能超过该限制。
    
    4. cap可以求出slice最大扩张容量,不能超出数组限制。0 <= len(slice) <= len(array),其中array是slice引用的数组。
    
    5. 切片的定义:var 变量名 []类型,比如 var str []string var arr []int。
    
    6. 如果 slice == nil,那么 len、cap 结果都等于 0。
    

    多维切片

    //第一种声明方式
    point := [][]int{}
    fmt.Printf("%T\n", point)
    point = [][]int{{1, 2}, {2, 23}, {1, 2, 3, 4, 5}}
    fmt.Println(point)
    point[1][1] = 222
    fmt.Println(point)
    //第二种声明方式
    point2 := make([][]int, 2, 4)
    fmt.Printf("%T %d %d\n", point2, len(point2), cap(point2))
    point2 = append(point2, []int{1, 1, 1, 1, 1})
    fmt.Println(point2)
    
    [][]int
    [[1 2] [2 23] [1 2 3 4 5]]
    [[1 2] [2 222] [1 2 3 4 5]]
    [][]int 2 4
    [[] [] [1 1 1 1 1]]
    

    切片和数组的传值类型对比

    //改变slice02会影响到slice01,因为slice01赋值给slice02其实是把地址给了slice02,他俩指向同一片内存空间
    var slice01 []int = []int{1, 2, 3, 4}
    var slice02 []int = slice01
    fmt.Println(slice01, slice02)
    slice02[0] = 10000
    fmt.Println(slice01, slice02)
    slice02 = []int{1, 1, 1}      //这里改变slice02,相当于把slice02重新指向了一个新地址
    fmt.Println(slice01, slice02) //所以slice01不会跟着变了
    
    //改变arr02就不会影响到arr01,arr01和arr02指向不同的内存空间
    var arr01 [5]int = [5]int{1, 1, 1, 1, 1}
    var arr02 = arr01
    arr02[0] = 222
    fmt.Println(arr01, arr02)
    
    [1 2 3 4] [1 2 3 4]
    [10000 2 3 4] [10000 2 3 4]
    [10000 2 3 4] [1 1 1]
    [1 1 1 1 1] [222 1 1 1 1]
    

    sort模块的排序函数

    sort模块可以对切片进行排序,请查官网手册的sort模块

    num := []int{2, 4, 3, 6, 678, 31}
    sort.Ints(num)
    fmt.Println(num)
    
    str := []string{"a1a", "asd", "34", "hhh"}
    sort.Strings(str)
    fmt.Println(str)
    
    fnum := []float64{-1, 2.2, 391.01, 0.0, 0}
    sort.Float64s(fnum)
    fmt.Println(fnum)
    
    [2 3 4 6 31 678]
    [34 a1a asd hhh]
    [-1 0 0 2.2 391.01]
    

    映射

    映射,相当于其它语言的hash-map。是key/value对

    定义

    //只定义不初始化,那么它是一个值为nil的映射
    var score map[string]int
    fmt.Printf("%T %#v\n", score, score)
    if score == nil {
        fmt.Println("score == nil")
    }
    
    //结果
    map[string]int map[string]int(nil)
    score == nil
    

    字面量初始化

    //使用字面量的形式初始化得到的是空映射
    var score2 = map[string]int{}
    if score2 != nil {
        fmt.Println("score2 != nil")
    }
    score2 = map[string]int{"小李": 10, "小王": 20, "小张": 30}
    fmt.Println(score2)
    //使用make方法初始化映射
    var score3 = make(map[string]int) //也是个空映射
    fmt.Println(score3)
    if score3 != nil {
        fmt.Println("score3 != nil")
    }
    
    //结果
    score2 != nil
    map[小张:30 小李:10 小王:20]
    map[]
    score3 != nil
    

    操作-增、删、改、查

    var price = map[string]int{"大米": 20, "白面": 30, "豆油": 40}
    p := price["大米"]
    fmt.Printf("%T %#v %v\n", p, p, p)
    //访问不存在的key值
    value := price["粉笔"]
    fmt.Printf("%T %v \n", value, value)
    //如果price中本来就有一个key/value,的值等于0.就会混淆
    //所以
    v, ok := price["粉笔"]
    fmt.Printf("v:%T %v ; ok:%T %v\n", v, v, ok, ok)
    //通过ok的值来判断key=粉笔是否存在
    if ok {
        fmt.Println(v)
    }
    //也可以把v,ok变成局部变量
    if v, ok := price["粉笔"]; ok {
        fmt.Println(v)
    } else {
        fmt.Println("粉笔不存在")
    }
    
    //结果
    int 20 20
    int 0 
    v:int 0 ; ok:bool false
    粉笔不存在
    

    var price = map[string]int{"大米": 20, "白面": 30, "豆油": 40}
    price["大米"] = 100
    fmt.Println(price)
    
    //结果
    map[大米:100 白面:30 豆油:40]
    

    映射是无序的,先添加的也不一定就是在前面

    var price = map[string]int{"大米": 20, "白面": 30, "豆油": 40}
    price["粉笔"] = 10
    fmt.Println(price) 
    
    //结果
    map[大米:20 白面:30 粉笔:10 豆油:40]
    

    var price = map[string]int{"大米": 20, "白面": 30, "豆油": 40}
    delete(price, "白面")
    fmt.Println(price)
    
    //结果
    map[大米:20 豆油:40]
    

    获取映射的长度用 len()

    var price = map[string]int{"大米": 20, "白面": 30, "豆油": 40}
    fmt.Println(len(price))
    

    遍历

    映射是无序的,遍历出来的顺序和添加时的顺序不一样

    var price = map[string]int{"大米": 20, "白面": 30, "豆油": 40}
    for k, v := range price {
        fmt.Printf("%v=%v \n", k, v)
    }
    
    //结果
    白面=30 
    豆油=40 
    大米=20
    

    映射是无序的

    go的映射是使用hashtable实现的,key和value建立关系是通过计算key,把key转化成一个int索引来和value 一 一映射,但是这个由key计算出的int值是不确定的,以至于映射是无序的

    关于key和value的类型

    key的类型至少可以进行 == != 的运算

    key的类型可以是:bool int string array 不能是slice不能是map

    value可以是任意类型,包括slice map

    //定义
    var user map[string]map[string]string
    //初始化
    user = map[string]map[string]string{"小胖": {"班级": "3年1班", "年龄": "10岁"}, "吴亦凡": {"班级": "大一", "年龄": "18岁"}}
    fmt.Println(user)
    //元素的操作-增加
    user["美美"] = map[string]string{"班级": "6年一班", "年龄": "13岁"}
    fmt.Println(user)
    //元素的操作-改
    user["美美"]["班级"] = "4年5班"
    fmt.Println(user)
    //元素的操作-删除
    delete(user["美美"], "年龄")
    fmt.Println(user)
    
    //结果
    map[吴亦凡:map[年龄:18岁 班级:大一] 小胖:map[年龄:10岁 班级:3年1班]]
    map[吴亦凡:map[年龄:18岁 班级:大一] 小胖:map[年龄:10岁 班级:3年1班] 美美:map[年龄:13岁 班级:6年一班]]
    map[吴亦凡:map[年龄:18岁 班级:大一] 小胖:map[年龄:10岁 班级:3年1班] 美美:map[年龄:13岁 班级:4年5班]]
    map[吴亦凡:map[年龄:18岁 班级:大一] 小胖:map[年龄:10岁 班级:3年1班] 美美:map[班级:4年5班]]
    

    映射的练习

    计数

    var stack = []string{"钢笔", "钢笔", "铅笔", "钢笔", "毛笔", "毛笔", "钢笔", "毛笔", "毛笔"}
    var count = map[string]int{}
    for _, v := range stack {
        // if _, ok := count[v]; !ok {
        //  count[v] = 1
        // } else {
        //  count[v] += 1
        // }
        //优化后
        count[v] += 1
    }
    fmt.Println(count)
    
    //结果
    map[毛笔:4 钢笔:4 铅笔:1]
    

    统计每个英文字母出现的次数

    var char = `I say to you today, my friends.
    And so even though we face the difficulties of today and tomorrow, I still have a dream. It is a dream deeply rooted in the American dream.
        I have a dream today!`
    var arr = map[rune]int{}
    for _, v := range char {
        if v >= 'A' && v <= 'Z' || v >= 'a' && v <= 'z' {
            arr[v] += 1
        }
    }
    fmt.Printf("%#v\n", arr)
    fmt.Println("------------------------------------------------")
    for key, cnt := range arr {
        fmt.Printf("%c : %v\n", key, cnt)
    }
    
    //结果
    map[int32]int{65:2, 73:4, 97:16, 99:3, 100:13, 101:18, 102:5, 103:1, 104:6, 105:8, 108:4, 109:7, 110:6, 111:13, 112:1, 114:9, 115:6, 116:12, 117:3, 118:3, 119:2, 121:7}
    ------------------------------------------------
    A : 2
    v : 3
    l : 4
    I : 4
    a : 16
    t : 12
    d : 13
    m : 7
    f : 5
    r : 9
    n : 6
    g : 1
    s : 6
    y : 7
    o : 13
    u : 3
    i : 8
    c : 3
    e : 18
    h : 6
    w : 2
    p : 1
    

    相关文章

      网友评论

          本文标题:Go语言基础

          本文链接:https://www.haomeiwen.com/subject/rtqtbltx.html