go中基本数据结构以及条件控制语句的使用

84 阅读8分钟

Go语言使用包来组织源代码的,并实现命名空间的管理,任何一个Go语言程序必须属于一个包,即每个go程序的开头要写上package <pgname>
​
// 定义包
  - 创建一个文件夹
  - 内部有很多go文件
  - go文件第一行声明包名,同一个包内包名必须一致
  - 在一个包下变量和函数是共享的(一个包相当于一个go文件夹,所以在同一个包下不能重复定义函数和变量)
  - 除了main包其他包都是被导入使用的
  - 无论是函数和变量,大写字母开头表示可以通过包导出使用
​
// 包的引用格式
1、标准引用:import fmt;
2、省略方式的引用 import . fmt,这种引用方式相当于把包的命名空间合并到当前程序的命名空间,因此前缀不需要加包名;
3、仅执行包的初始化函数 import _ fmt,也就是只执行报的init函数;
4、单行引用引用以及多行引用;

字符串

// 字符串底层是一个byte切片,所以可以和[]byte类型相互转换。字符串是不能修改的,字符串是由byte字节组成,所以字符串的长度是byte字节的长度。 rune类型用来表示utf8字符,一个rune字符由一个或多个byte组成
// byte:uint8别名,只能存放八个二进制字节第一个二进制为表正负代表了所有的ASCII 码, rune:int32别名存放32个二进制为位,当需要处理中文、日文或者其他复合字符时,则需要用到 rune 类型。rune 类型等价于 int32 类型。
s := "我爱中国"
z :=[]byte(s)
x :=[]rune(s)
fmt.Println(z)  // [230 136 145 231 136 177 228 184 173 229 155 189]一个中文字符占3个bytes即uint8
fmt.Println(x)  // [25105 29233 20013 22269]一个中文字符对应一个int32
fmt.Println(string(x))  // 我爱中国
// 循环字符
s := "你好家伙"
var r[]rune = []rune(s)
for i :=0;i<len(r);i++{
    fmt.Println(r[i])
    fmt.Println(string(r[i]))
}
​
// 获取每个字节的bytes,
s := "dsfhfdisaf"
for i :=0;i<len(s);i++{
    fmt.Println(s[i])  // 获取每一个字符对应的字节
    fmt.Println(string(s[i]))  // 通过字节转成字符
}
// byte(uint8),rune(uint32四个字节)这两种类型来代表字节和字符
// 字符串长度,len()方法统计的是字节长度,utf8.RuneCountInString()统计字符长度// 遍历字符串
for _, val := range s{
        fmt.Println(string(val))
    }
​
// 从字符串中截取内容,字符串的截取是按照byte进行截取,下标截取遵从左闭右开原则
s := "hello我爱中国"
fmt.Println(s[3:8])  // 中文字符三个单个字节,所以是到第八位
strings.Index(s, "o")  // 查找字符“o”在字符串s中第一次出现的位置
strings.HasPrefix(s, "he")  // 判断字符串s是否以指定字符串“he”开头
strings.HasSuffix(s, "国")  // 判断字符串结尾
strings.ToLower(s)  // 将字符串s全部变小写
strings.ToUpper(s)  // 将字符串s全部变大写

数组

// 数组是同一种数据类型元素的集合。 在Go语言中,数组从声明时就确定,使用时可以修改数组成员,但是数组大小不可变化,数组是值类型,赋值和传参会复制整个数组
// 定义数组:var 数组变量名 [元素数量] 数组中数值类型
var a [5]int
// 数组的长度必须是常量,并且长度是数组类型的一部分;一旦定义,长度不能变。 [5]int和[10]int是不同的类型。数组可以通过下标进行访问,下标是从0开始,最后一个元素下标是:len-1,访问越界(下标在合法范围之外),则触发访问越界,会panicvar testArray [3]int                        //数组会初始化为int类型的零值
var numArray = [3]int{1, 2}                 //使用指定的初始值完成初始化
var cityArray = [3]string{"北京", "上海", "深圳"} //使用指定的初始值完成初始化
fmt.Println(testArray)                      //[0 0 0]
fmt.Println(numArray)                       //[1 2 0]
fmt.Println(cityArray)                      //[北京 上海 深圳]
var numArray = [...]int{1, 2}   // ...符号指定的长度等于文字中元素的数量
var cityArray = [...]string{"北京", "上海", "深圳"}
fmt.Println(testArray)                          //[0 0 0]
fmt.Println(numArray)                           //[1 2]
fmt.Printf("type of numArray:%T\n", numArray)   //type of numArray:[2]int
​
a := [...]int{1: 1, 3: 5}  // 根据下标赋初值
fmt.Println(a)                  // [0 1 0 5]// 遍历数组
for i :=0;i<len(testarray);i++{
    fmt.println(testarray[i])
}
for ind, val :=range a {
    fmt.println(ind, val)
}
​
// 二维数组
a := [3][2]string{
    {"北京", "上海"},
    {"广州", "深圳"},
    {"成都", "重庆"},
}
fmt.Println(a) //[[北京 上海] [广州 深圳] [成都 重庆]]
fmt.Println(a[2][1]) //支持索引取值:重庆for _, val := range a{
    for _, val2 :=range val{
        fmt.println(val)
    }
}

切片

/*切片(Slice)是一个拥有相同类型元素的可变长度的序列。它是基于数组类型做的一层封装。它非常灵活,支持自动扩容,切片本身不拥有任何数据只是对现有数的一个引用即指向现存数据的地址。
切片是一个引用类型,它的内部结构包含地址、长度和容量。切片一般用于快速地操作一块数据集合,切片的长度是可变的,容量的大小是由引用的现有数据长度决定的;在函数中使用切片,修改切片中的值会导致原有数据的值改变
*/
// 切片定义:var 切片名 []类型
var a []string              //声明一个字符串切片
var b = []int{}             //声明一个整型切片并初始化
var s []int=make([]int, size, cap)     //通过make创建切片
// 切片拥有自己的长度和容量,我们可以通过使用内置的len()函数求长度,使用内置的cap()函数求切片的容量。
// 切片会指向现有数据的内存地址,修改现有数据会改变切片内数据,同时修改切片内数据也会修改现有数据
var testarr = [...]int{1,3,5}
var c[] int
c = testarr[:]
c[0] = 99
fmt.Println(c)  // [99 3 5]
fmt.Println(testarr)  // [99 3 5]var testarr = [...]int{1,3,5}
var c[] int
c = testarr[:]
fmt.Printf("c的类型是%T", c)  // c的类型是[]int
fmt.Printf("c的类型是%T", testarr)  //c的类型是[3]int// 追加切片
// 如果切片容量不够满足现有,go语言会申请一个新底层数组大小为原来切片容量的两倍并且当前切片指向新底层数组的地址,原有数据会被复制到新的数组上,此时切片就会和原有数组脱离,原数组再变化就不会影响切片;也就是说,容量的用途是:在数据拷贝和内存申请的消耗与内存占用之间提供一个权衡。
var sl []int = make([]int, 3, 4)
sl = append(sl, 99)
fmt.Println(len(sl))  // 4
fmt.Println(sl)  // [0 0 0 99]
fmt.Println(cap(sl))  // 4// 已经达到最大容量,如果再追加长度加1,但容量会翻倍,基于原来的容量翻倍
sl = append(sl, 8)
fmt.Println(len(sl))  //5
fmt.Println(sl)  // [0 0 0 99 8]
fmt.Println(cap(sl))  // 8

map

/*
map是一种无序的基于key-value的数据结构,Go语言中的map是引用类型,必须初始化才能使用。
定义map:map[KeyType]ValueType通常使用make()函數來分配内存make(map[KeyType]ValueType, [cap]),其中cap表示map的容量,该参数虽然不是必须的map可以自动扩容,但是我们应该在初始化map的时候就为其指定一个合适的容量。
*/
scoreMap := make(map[string]int, 8)
scoreMap["张三"] = 90
scoreMap["小明"] = 100
fmt.Println(scoreMap)
fmt.Println(scoreMap["小明"])
fmt.Printf("type of a:%T\n", scoreMap)  // type of a:map[string]int// 同時map在声明的时候也支持赋初值
userInfo := map[string]string{
    "username": "hector",
    "password": "123456",
}
​
// 判断map中某个键是否存在使用value, ok := map[key],value为改键在map中的对应值,不存在则为0
scoreMap := make(map[string]int)
scoreMap["张三"] = 90
scoreMap["小明"] = 100// 如果key存在ok为true,v为对应的值;不存在ok为false,v为值类型的零值
v, ok := scoreMap["张三"]
if ok {
    fmt.Println(v)
} else {
    fmt.Println("查无此人")
}
​
// 遍历map,遍历map时的元素顺序与添加键值对的顺序无关。
scoreMap := make(map[string]int)
scoreMap["张三"] = 90
scoreMap["小明"] = 100
scoreMap["娜扎"] = 60
for k, v := range scoreMap {
    fmt.Println(k, v)
}
​
// 删除map中某个键值对,使用delete()内建函数从map中删除一组键值对:delelte(map, key)

流程控制

// go中判断的格式:
if 表达式1 {
    分支1
} else if 表达式2 {
    分支2
} else{
    分支3
}
// Go语言规定与if匹配的左括号必须与if和表达式放在同一行,左括号放在其他位置会触发编译错误。 同理,与else匹配的{也必须与else写在同一行,else也必须与上一个if或else if右边的大括号在同一行// go中支持索引循环,分三段每一段之间使用;间隔,第一段索引初始值,第二段条件,第三段索引自增或自减,其中第一段,第三段都是可以省略但还是使用分号间隔
for(i :=0; 条件; i++){
    // 使用索引取值    
}
// for循环可以通过break、goto、return、panic语句强制退出循环// go使用range进行迭代取值
Go语言中可以使用for range遍历数组、切片、字符串、map 及通道(channel)。 通过for range遍历的返回值有以下规律:
数组、切片、字符串返回索引和值。
map返回键和值。
通道(channel)只返回通道内的值
for ind, val := range testarray{
    print(ind, val)
}
​
// 使用switch语句可方便地对大量的值进行条件判断, 每个switch只能有一个default分支
func switchdemo (a int){
    switch a {
    case 20:
        fmt.Println("")
    case 30:
        fmt.Println("数值为%s",a)
    default:
        fmt.Println("数值不对")
    }
}
// switch另一种使用switch后不跟变量,在case后边跟变量的条件判断,fallthrough只能放在case块中的结尾位置,fallthrough语法执行当前满足条件的case和下一个case