GO语言学习(三)
控制结构
像我在java中,语言代码逻辑的控制经常会用的就是判断if,循环for,条件switch等,那么在go语言中,其实也有这些控制语句,接下来,我们可以去玩一下
if 条件语句
if 语句是条件语句,它根据布尔值的表达式来决定选择哪个分支执行,这个和java一样
package main
import "fmt"
func main() {
i := 10
if i > 10 {
fmt.Println("i>10")
} else {
fmt.Println("i<=10")
}
}
输出结果:i<=10
还有其他写法,比如:
func main() {
i:=6
if i >10 {
fmt.Println("i>10")
} else if i>5 && i<=10 {
fmt.Println("5<i<=10")
} else {
fmt.Println("i<=5")
}
}
func main() {
if i:=6; i >10 {
fmt.Println("i>10")
} else if i>5 && i<=10 {
fmt.Println("5<i<=10")
} else {
fmt.Println("i<=5")
}
}
最后一种其实可读性不好,所以不建议用,整体用法和java相差不大,所以我就不详细介绍了
switch 选择语句
如java中一样,switch表示多个分支,如果有很多分支的话,选择 switch 会更方便,但写法上有些差距,重显著的一点就是,java中每个case完成后,若不需要向下判断,则需要加break来阻止向下查询,而Go 语言的 switch 在默认情况下,case 最后自带 break,而且在go中,case可以是表达式
switch i:=6;{
case i>10:
fmt.Println("i>10")
case i>5 && i<=10:
fmt.Println("5<i<=10")
default:
fmt.Println("i<=5")
}
如果你真的有需要,的确需要执行下一个紧跟的 case 怎么办呢
switch j:=1;j {
case 1:
fallthrough
case 2:
fmt.Println("1")
default:
fmt.Println("没有匹配")
如上面的代码,需要用到关键字fallthrough
switch 后的表达式也没有太多限制,是一个合法的表达式即可,也不用一定要求是常量或者整数,可见 Go 语言的 switch 语句非常强大且灵活
for 循环语句
我们先写一个go语言的循环语句:
package main
import "fmt"
func main() {
sum := 0
for i := 1; i <= 100; i++ {
sum += i
}
fmt.Println("the sum is", sum)
}
输出结果:5050
解析for结构:
- 第一部分是一个简单语句,一般用于 for 循环的初始化,比如这里声明了一个变量,并对 i:=1 初始化;
- 第二部分是 for 循环的条件,也就是说,它表示 for 循环什么时候结束。这里的条件是 i<=100;
- 第三部分是更新语句,一般用于更新循环的变量,比如这里 i++,这样才能达到递增循环的目的。
与java不同的是,go中的for结构可以省略很多部分:
sum:=0
i:=1
for i<=100 {
sum+=i
i++
}
fmt.Println("the sum is",sum)
在 Go 语言中,同样支持使用 continue、break 控制 for 循环:
- continue 可以跳出本次循环,继续执行下一个循环。
- break 可以跳出整个 for 循环,哪怕 for 循环没有执行完,也会强制终止。
集合类型
在实际需求中,我们会有很多同一类型的元素放在一起的场景,这就是集合,例如 100 个数字,10 个字符串等。在 Go 语言中,数组(array)、切片(slice)、映射(map)这些都是集合类型,用于存放同一类元素
Array(数组)
数组存放的是固定长度、相同类型的数据,而且这些存放的元素是连续的。所存放的数据类型没有限制,可以是整型、字符串甚至自定义。
数组声明
注意:[5]string 和 [4]string 不是同一种类型,也就是说长度也是数组类型的一部分。
array:=[5]string{"a","b","c","d","e"}
数组在内存中都是连续存放的
从图中可以看到,数组的元素都是按下标排列的,所以我们要取数组中的元素也很简单,可以直接通过下标获取
package main
import "fmt"
func main() {
array := [5]string{"a", "b", "c", "d", "e"}
fmt.Println(array[2])
}
数组的长度可以省略,这个时候 Go 语言会自动根据大括号 {} 中元素的个数推导出长度
array:=[...]string{"a","b","c","d","e"}
省略数组长度的声明只适用于所有元素都被初始化的数组,如果是只针对特定索引元素初始化的情况,就不适合了
package main
import "fmt"
func main() {
array := [5]string{1: "b", 3: "d"}
for i := 0; i < 5; i++ {
fmt.Printf("数组索引:%d,对应值:%s\n", i, array[i])
}
}
输出结果:
数组索引:0,对应值:
数组索引:1,对应值:b
数组索引:2,对应值:
数组索引:3,对应值:d
数组索引:4,对应值:
数组循环
Go 语言的新型循环for range,所以,我将上面的循环改造一下
package main
import "fmt"
func main() {
array := [5]string{1: "b", 3: "d"}
//i数组的索引,v是数组的值
for i, v := range array {
fmt.Printf("数组索引:%d,对应值:%s\n", i, v)
}
}
若值不需要,还可以用以下写法
for _,v:=range array{
fmt.Printf("对应值:%s\n", v)
}
Slice(切片)
切片和数组类似,可以把它理解为动态数组。切片是基于数组实现的,它的底层就是一个数组。对数组任意分隔,就可以得到一个切片
基于数组生成切片
package main
import "fmt"
func main() {
array := [5]string{"a", "b", "c", "d", "e"}
slice := array[2:5]
fmt.Println(slice)
}
输出结果:[c d e]
上面就是一个切片的操作,它包含从数组 array 的索引 2 开始到索引 5 结束的元素
注意:这里是包含索引 2,但是不包含索引 5 的元素,即在 : 右边的数字不会被包含。
上面那种写法还可以变化一下
slice := array[2:5] 等于。slice := array[2:]
切片修改
除了可以从一个数组得到切片外,还可以声明切片,比较简单的是使用 make 函数
slice1:=make([]string,4)
上面就是声明了一个切片长度为4的切片,切片不止可以指定长度,还可以指定容量
slice1:=make([]string,4,8)
- 长度:切片内元素的个数
- 容量:当前切片可容纳的最大元素个数,若超出容量,则需扩容
切片不仅可以通过 make 函数声明,也可以通过字面量的方式声明和初始化
slice1:=[]string{"a","b","c","d","e"}
fmt.Println(len(slice1),cap(slice1))
Append
我们可以通过内置的 append 函数对一个切片追加元素,返回新切片
//追加一个元素
slice2:=append(slice1,"f")
//多加多个元素
slice2:=append(slice1,"f","g")
//追加另一个切片
slice2:=append(slice1,slice...)
小技巧:在创建新切片的时候,最好要让新切片的长度和容量一样,这样在追加操作的时候就会生成新的底层数组,从而和原有数组分离,就不会因为共用底层数组导致修改内容的时候影响多个切片
Map(映射)
在 Go 语言中,map 是一个无序的 K-V 键值对集合,结构为 map[K]V。其中 K 对应 Key,V 对应 Value。map 中所有的 Key 必须具有相同的类型,Value 也同样,但 Key 和 Value 的类型可以不同。此外,Key 的类型必须支持 == 比较运算符,这样才可以判断它是否存在,并保证 Key 的唯一
Map 声明初始化
创建一个 map 可以通过内置的 make 函数
nameAgeMap:=make(map[string]int)
它的 Key 类型为 string,Value 类型为 int。有了创建好的 map 变量,就可以对它进行操作了
package main
import "fmt"
func main() {
namemap := make(map[string]int)
namemap["laowan"] = 2
fmt.Println(namemap)
}
输出结果:map[laowan:2]
除了可以通过 make 函数创建 map 外,还可以通过字面量的方式
nameAgeMap:=map[string]int{"laowan":2}
在创建 map 的同时添加键值对,如果不想添加键值对,使用空大括号 {} 即可,要注意的是,大括号一定不能省略
Map 获取和删除
map 的操作和切片、数组差不多,都是通过 [] 操作符,只不过数组切片的 [] 中是索引,而 map 的 [] 中是 Key
package main
import "fmt"
func main() {
namemap := make(map[string]int)
namemap["laowan"] = 2 //1、在map中新增一个键值对
intval := namemap["laowan"] //2、获取map中指定key的值
intval1 := namemap["laowan1"] //3、获取不存在的键值对,获取的值是0
fmt.Println(namemap, intval, intval1)
}
改造一下,当不存在时不打印对于值
intval1, ok := namemap["laowan1"]
if ok {
fmt.Println(intval1)
}
如果要删除 map 中的键值对,使用内置的 delete 函数即可,比如要删除 nameAgeMap 中 Key 为laowan的键值对
delete(nameAgeMap,"laowan")
遍历 Map
map 是一个键值对集合,它同样可以被遍历,在 Go 语言中,map 的遍历使用 for range 循环
package main
import "fmt"
func main() {
namemap := make(map[string]int)
namemap["laowan"] = 2
namemap["laowan1"] = 3
namemap["laowan2"] = 4
namemap["laowan3"] = 5
for k, v := range namemap {
fmt.Println("key=", k, ",val=", v)
}
}
需要注意的是 map 的遍历是无序的,也就是说你每次遍历,键值对的顺序可能会不一样。如果想按顺序遍历,可以先获取所有的 Key,并对 Key 排序,然后根据排序好的 Key 获取对应的 Value
package main
import (
"fmt"
"sort"
)
func main() {
namemap := make(map[string]int)
namemap["laowan"] = 2
namemap["laowan1"] = 3
namemap["laowan2"] = 4
namemap["laowan3"] = 5
slice1 := make([]string, 0, len(namemap)) //声明长度为0,容量为map长度的切片
for k := range namemap { //遍历map,获取key值
slice1 = append(slice1, k) //将key值存入到切片中
}
//sort.Strings(slice1) //将key值正序排序
sort.Sort(sort.Reverse(sort.StringSlice(slice1))) //将key值倒序排序
for _, v := range slice1 {
i := namemap[v] //获取对应的val
fmt.Println(i)
}
}
Map 的大小
如上面的代码
len(namemap)
String 和 []byte
字符串 string 也是一个不可变的字节序列,所以可以直接转为字节切片 []byte
package main
import "fmt"
func main() {
s := "Hello laowan"
bs := []byte(s)
fmt.Println(bs)
}
输出结果:[72 101 108 108 111 32 108 97 111 119 97 110]
func main() {
s := "Hello laowan青"
bs := []byte(s)
fmt.Println(bs)
fmt.Println(s[0], s[1], s[14])
}
注意:在go中,一个中文占3个字节,如果你想把一个汉字当成一个长度计算,可以使用 utf8.RuneCountInString 函数