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
func main() {
    if i:=6; i >10 {
        fmt.Println("i>10")
    } else if  i>5 && i<=10 {
        fmt.Println("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

如果你真的有需要,的确需要执行下一个紧跟的 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结构:

  1. 第一部分是一个简单语句,一般用于 for 循环的初始化,比如这里声明了一个变量,并对 i:=1 初始化;
  2. 第二部分是 for 循环的条件,也就是说,它表示 for 循环什么时候结束。这里的条件是 i<=100;
  3. 第三部分是更新语句,一般用于更新循环的变量,比如这里 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 函数

相关