Go基础
go基础
// go镜像地址
https://goproxy.cn,direct
// 占位符
%s 字符串占位
%d 数字占位
%f 浮点数占位
%t 布尔类型
%p 指针
%v 自己寻找数据类型
%T 查看数据类型
%o 八进制
%x 十六进制
%b 二进制
%u rune
%c byte类型
%q 打印出来的值会加引号
变量
一个作用域内标识符只能声明一次
{}来显示声明一个作用域
- 自定义变量
package main
import "fmt"
var aa = 3 // 函数外定义变量必须加var
var ss = "kkk"
var ( // 省略写法,不用每个变量都写一个var
dd = 3
ee = "kkk"
)
func variableShorter() {
a, b, c, d := 3, 4, true, "twg" // 函数内定义变量使用:=
b = 5 // 再次对变量名赋值不需要加冒号
fmt.Println(a, b, c, d)
}
func main() {
fmt.Println("hello world")
variableShorter()
fmt.Println(aa, ss, ee)
}
- 内置变量
bool // 布尔
string // 字符串
(u)int // 无符号整数
int // 有符号整数
(u)int16 // 无符号规定整数长度
uintptr // 指针
byte // 字节,8位,unit8别名,存放1位ASCII,如英文
rune // unit32别名,存放多字节字符,如中文,返回Unicode码点值
float32 // 长度32位
complex64 // 复数类型
complex128 // 复数
// 字符字节说明:
字符串 = 字符 + 字符
字符 = 字节 + 字节
英文字节 = 1字节(使用byte or unit8)
中文字节 = 1 ~ 4字节(rune or int32)
1 字节 = 8 比特位
1字节 = 8unit
4字节 = 32unit = rune
// 编码格式:
Unicode:可以包含世界上任意语言或者字符
UTF-8:是Unicode的一种实现方式,定义了字符串以何种形式存储在内存中,使用1~4为每个字符编码
- 强制转换
var a,b int = 3,4
var c int = math.Sqrt(a*a + b*b) //因为sqrt是计算浮点型,但a,b又是int,所以这里计算错误,用如下方式
var c int = int(math.Sqrt(float64(a*a + b*b))) //计算浮点型就转换为浮点型,定义c为int类型就转为int类型
常量
常量变量名写为大写(大家默认的一种写法)
const filename = "abc.txt",
const数值可作为各种类型使用
不想要后面修改的变量
package main
import (
"fmt"
"math"
)
func consts() {
const filename string = "abc.txt" // 定义一个常量指定为字符串类型
const a, b = 3, 4 // 定义一个常量不指定类型
var c int
c = int(math.Sqrt(a*a + b*b)) // 以往的计算中Sqrt只能计算浮点型,但现在a,b为常量,也就能被sqrt计算
fmt.Println(filename, c) // abc.txt 5
}
func main() {
consts()
}
- 多个常量写法
func consts() {
const (
filename string = "abc.txt"
a, b = 3, 4
)
}
// 当常量值一致,可以不进行定义
func consts() {
const (
C7 int = 1
C8
C9
)
}
作用域
调用的变量向上找变量名对应的值
可以在一个结构体中定义相同变量名
如果变量有定义,但是没有调用,在后续执行会报错
package main
import "fmt"
func main() {
// 作用域:定义变量和标识符可以使用的范围
// 在Go中用大括号来定义作用域范围
// 子语句块可以使用父语句块中的标识符,父不能使用子的
outer := 1
{
fmt.Println(outer) // 1
inner2 := 2
{
fmt.Println(inner2) // 2
fmt.Println(outer) // 1
{
inner3 := 3
fmt.Println(outer, inner2, inner3) // 1 2 3
}
}
}
}
数据类型
枚举
const + iota
- 普通枚举类型
func enums() {
const (
cpp = iota // iota为自增赋值,从0开始
_ // 这里_为占位跳过
python
golang
javascript
)
fmt.Println(cpp, python, golang, javascript)
}
func main() {
enums() // 0 2 3 4
}
- 自增值枚举类型
func enums() {
const (
b = 1 << (10 * iota)
kb
mb
gb
tb
pb
)
fmt.Println(b, kb, mb, gb, tb, pb)
}
func main() {
enums() // 1 1024 1048576 1073741824 1099511627776 1125899906842624
}
布尔
布尔类型表示真假
标识符bool
可选值true/false
零值false
// 范例
func main() {
var zero bool // zero 为false
isBoy := true
isgirl := false
fmt.Println(zero, isgirl, isBoy) // false false true
}
与&&,或||,非!
// && 的运算规则
func main() {
// 操作
// 逻辑运算(与&&,或||,非!)
// aBool && bBool当两个都为True的时候结果为True
fmt.Println(true && false) // false
fmt.Println(true && false) // false
fmt.Println(true && false) // false
fmt.Println(true && false) // false
fmt.Println(true && true) // true
}
占位符打印
%T 打印类型
%t 占位符为bool类型
func main() {
// 操作
// 逻辑运算(与&&,或||,非!)
// aBool && bBool当两个都为True的时候结果为True
isBoy := true
isGirl := false
fmt.Printf("%T %t %t\n", isBoy, isBoy, isGirl) //bool true false
}
整数与数值运算
func main() {
// 整数类型
// 标识符:int/int*/uint*/uintper/byte/rune
// 字面量:十进制,八进制,十六进制
var age int = 31
fmt.Printf("%T %d\n", age, age) // int 31
fmt.Println(0777)
// 操作符
// 计算加减乘除
fmt.Println(1 + 2)
fmt.Println(3 - 10)
fmt.Println(3 * 9)
fmt.Println(9 / 2)
fmt.Println(9 % 2) // 1
age++
fmt.Println(age) // 32
age--
fmt.Println(age) // 31
// 位运算 二进制的运算 10 => 2
// & | ^ << >> &^
// 十进制 => 二进制
// 7 & 2 => 0111 & 0010 => 0010 => 2
// 7 | 2 => 0111 & 0010 => 0111 => 7
// 7 ^ 2 => 0111 & 0010 => 0101 => 7 两者不一样为1
// 2 << 1 => 0010 << 1 => 0100 => 4 将2左移1位
// 2 >> 1 => 0010 >> 1 => 0001 => 1 将2右移1位
// 7 &^ 2 => 0111 &^ 0010 => 0101 => 5 两个都是1变0,0和0是0,1和0是,1和0是1
fmt.Println(7 & 2) // 2
// 赋值运算(=,+=,-=,*=,/=,%=,&=,|=,^=,<<=,>>=,&^=)
age = 1
age += 3 // age = age + 3
fmt.Println(age) // 4
age /= 3
fmt.Println(age) // 1
// int/uint/byte/rune/int*
var intA int = 10
var uintB uint = 3
fmt.Println(intA + uintB) // 报错,不同类型不能计算,需要转换
fmt.Println(uint(intA) + uintB) // 将initA转换为uint计算
// fmt.Printf
// int/unit/int*/uint*
// byte,rune
var a byte = 'A'
var w rune = 'A'
fmt.Println(a, w) // 65 65
fmt.Printf("%T ", age) // int
fmt.Printf("%T ", a) // uint8
fmt.Printf("%T ", w) // int32
fmt.Println("%T %d %b %o %X\n", a, a, a, a, a)
}
浮点型
func main() {
// float32,float64
// 字面量:十进制表示法,科学技术法
// M E N = M * 10 ^ N
// 1.9E-1 => 0.19
var height float64 = 1.68
fmt.Println(height)
fmt.Printf("%T %f\n", height, height) // float64 1.680000
var weight float64 = 13.05e1
fmt.Println(weight) // 130.5
// 操作
// 算数运算(+,-,*,/,++,--)
fmt.Println(1.1 + 1.2)
}
字符串
func main() {
// "" => 可解释,也就是可以将\n等结果变为换行
// `` => 原生字符串,如果是\n结果输出也是\n
// 特殊字符 \n \f \t \r \b \v \f
var name string = "KK"
var desc string = `我来\t自湖南`
fmt.Println(desc) // 我来\t自湖南
fmt.Println(name) // KK
// 操作
// 算数运算符:+ (字符串连接)
fmt.Println("我叫" + "tcy") // 我叫tcy
// 赋值
s := "我叫"
s += "kk"
fmt.Println(s) // 我叫kk
// 字符号定义只能为Ascii
// 索引 0 - n-1 (n 字符串长度)
desc = "abcdef"
fmt.Printf("%T %c\n", desc[0], desc[0]) // uint8 a
// 切片
fmt.Printf("%T %s\n", desc[0:2], desc[0:2]) // string ab
// 长度查询
fmt.Println(len(desc)) // 6
s := "我爱中华人民共和国"
fmt.Println(len(s)) // 27 结果和数字不匹配
fmt.Println(utf8.RuneCountInString(s)) //9 获取unicode字符数量
}
字符串函数
fmt.Println(strings.Compare("abc", "bcccccac")) // 比较两个字符串,不相等为-1
fmt.Println(strings.Contains("abc", "ac")) // false 左边是否包含右边
fmt.Println(strings.Count("abcdakdjflk", "a")) // 右边字符出现次数
fmt.Println(strings.Fields("abc def \neeee\f")) // [abc def eeee] 按空白字符分隔(空格,\n,\r,\f,\t)
fmt.Println(strings.HasPrefix("abcddd", "ab")) // true 什么开头
fmt.Println(strings.HasSuffix("abcddd", "ab")) // false 什么结尾
fmt.Println(strings.Index("defasdjfj", "fas")) // 2 fas出现的索引位置
fmt.Println(strings.Index("sdfjsldjflk", "bsld")) // -1 因为不存在
fmt.Println(strings.LastIndex("abceabcd", "abc")) // 最后一个abc出现的索引位置
fmt.Println(strings.Split("ab;cd", ";")) // 指定分隔符进行分隔
fmt.Println(strings.Join([]string{"abc", "def", "deee"}, ";")) // 将切片中的元素以冒号连接
fmt.Println(strings.Repeat("abc", 3)) // abcabcabc
fmt.Println(strings.Replace("abcabcabcab", "ab", "xxx", 1)) // 将第一个匹配到的ab替换成xxx
fmt.Println(strings.Replace("abcabcabcab", "ab", "xxx", -1)) // 全部替换
fmt.Println(strings.ToLower("bacAcdkB")) // 全部转换为小写
fmt.Println(strings.ToUpper("ABDFdksjlfdj")) // 全部转换为大写
fmt.Println(strings.TrimSpace("abc bca u\nskdj")) // 去除空格字符
// 字符串转换
// 字符串 ==> 其他类型
// == bool
v, err := strconv.ParseBool("true") //
fmt.Println(v, err) // true <nil>
if v, err := strconv.ParseBool("eee"); err == nil {
fmt.Println(v)
} else {
fmt.Println(err)
}
// 字符串转换 ==> int类型
if v, err := strconv.Atoi("1023"); err == nil {
fmt.Println(v)
} else {
fmt.Println(err)
}
// 字符串 ==> float
v, err := strconv.ParseFloat("1.1", 64)
fmt.Println(v, err) // 1.1 <nil>
// 其他类型 ==> string
sd := fmt.Sprintf("%d", 12)
fmt.Printf("%T %v\n", sd, sd) // string 12
fmt.Println(strconv.FormatBool(false)) // 字符串类型的false
fmt.Println(strconv.Itoa(12)) // int类型转换为字符串
指针
- 取地址操作符&和取值操作符“”是一对互补操作符,&取出地址,“”根据地址取出地址指向的值
*操作符作为右值时,意义是取指针的值,作为左值时,也就是放在赋值操作符的左边时,表示 a 指针指向的变量。其实归纳起来,
*操作符的根本意义就是操作指针指向的变量。当操作在右值时,就是取指向变量的值,当操作在左值时,就是将值设置给指向的变量。
func main() {
A := 2
B := A
B = 3
fmt.Println(A, B) // 2 3
// 指针
var cc *int = &A // 拿到A变量对应值的内存地址赋值给cc
c := &A
fmt.Printf("%T %T\n", cc, c) // *int *int
fmt.Println(*cc) // *cc,拿到内存地址对应的值
}
用户输入
func main() {
// 当定义变量是什么类型,最好就根据指定类型去书写
var name string
fmt.Print("请输入你的名字:") // 请输入你的名字:tcy
fmt.Scan(&name)
fmt.Println("名字", name) // 名字 tcy
var age int
fmt.Print("请输入你的年龄:")
fmt.Scan(&age)
fmt.Println("年龄", age)
var height float64
fmt.Print("请输入你的身高: ")
fmt.Scan(&height)
fmt.Println("身高", height)
}
if判断
If条件里赋值的变量作用域就在这个if语句里
- 判断文件是否存在(方式一)
import (
"fmt"
"io/ioutil"
)
func main() {
const filename = "abc.txt"
contents, err := ioutil.ReadFile(filename) // 读取文件
if err != nil { // 如果文件不存在
fmt.Println(err)
} else { // 反之文件存在
fmt.Println("%s\n", contents)
}
}
- 判断文件是否存在(方式二)
import (
"fmt"
"io/ioutil"
)
func main() {
const filename = "abcd.txt"
if contents, err := ioutil.ReadFile(filename); err != nil {
fmt.Println(err)
} else {
fmt.Println("%s\n", contents)
}
}
- if-else判断
func main() {
var yes string
fmt.Print("有卖西瓜的吗:")
fmt.Scan(&yes)
fmt.Println("老婆的想法:")
fmt.Println("十个西瓜")
if yes == "Y" || yes == "y" {
fmt.Println("一个西瓜")
}
fmt.Println("老公的想法:")
if yes == "Y" || yes == "y" {
fmt.Println("一个包子")
} else {
fmt.Println("十个包子")
}
}
- if - else if - else
func main() {
var score int
fmt.Print("请输入你的成绩:")
fmt.Scan(&score)
if score >= 90 {
fmt.Println("A")
} else if score >= 80 {
fmt.Println("B")
} else if score >= 70 {
fmt.Println("C")
} else if score >= 60 {
fmt.Println("D")
} else {
fmt.Println("E")
}
}
switch
switch里的case后面自动加了break
- switch ==> case
func main() {
var yes string
fmt.Print("有卖西瓜的吗:")
fmt.Scan(&yes)
switch yes {
case "y", "Y":
fmt.Println("买一个西瓜1")
case "N":
fmt.Println("啥也不买")
default:
fmt.Println("十个包子") // 随便输入什么都输出这个结果
}
}
- switch ==> case
func main() {
var score int
fmt.Print("请输入你的成绩:")
fmt.Scan(&score)
switch {
case score >= 90:
fmt.Println("A")
case score >= 80:
fmt.Println("B")
case score >= 70:
fmt.Println("C")
case score >= 60:
fmt.Println("D")
default:
fmt.Println("E")
}
}
for循环
- 写法一:
func main() {
// 索引 => 记录已经加到的n
// 记录结果
result := 0
i := 1
for i <= 100 {
result += i
i++
}
fmt.Println(result)
}
- 写法二:
func main() {
// 索引 => 记录已经加到的n
// 记录结果
result := 0
// 初始化子语句,条件子语句,后置子语句
for i := 1; i <= 100; i++ {
result += i
}
fmt.Println(result)
}
- 写法三:
- for range
func main() {
desc := "我爱中国"
for i, ch := range desc {
fmt.Printf("%d %T %q\n", i, ch, ch)
}
}
- for + if + continue + break
func main() {
fmt.Println("continue: ")
for i := 0; i < 5; i++ {
if i == 3 {
continue
}
fmt.Println(i)
}
fmt.Println("break: ")
for i := 0; i < 5; i++ {
if i == 3 {
break
}
fmt.Println(i)
}
}
goto
使用场景:当多个循环嵌套,break只能退出本层循环,但goto可以跳转到任意位置。
- 案例一:
func main() {
var yes string
fmt.Print("有卖西瓜的吗? (y/Y) ")
fmt.Scan(&yes)
if yes != "y" && yes != "Y" {
fmt.Println("买十个包子")
goto END // 跳转到END
}
fmt.Println("买一个西瓜")
END: // 从上面位置直接跳转到END,会忽略打印买一个西瓜
}
- 案例二:
// 判断大小,当满足条件,跳转到goto指定位置
func main() {
result := 0
i := 1
START:
if i > 100 {
goto FOREND // 当i大于100符合if判断,调到FOREND
}
result += i
i++
goto START // 当i小于100,跳到START
FOREND:
fmt.Println(result) // 5050
}
数组
相同数据类型,固定长度的序列
数据项为数组的元素,数组长度必须为非负整数的常量,长度也是类型一部分。
声明:
- 数组声明需要指定组元素的类型以及存储元素的数量(长度)
- 定义完成后,长度不可修改
func main() {
var nums [10]int // 定义数组,变量 元素长度,元素类型
fmt.Printf("%T", nums)
}
- 数组字面量&简短声明
func main() {
var nums [10]int // 定义数组,变量 元素长度,元素类型
// 字面量
nums = [10]int{10, 20, 30}
fmt.Println(nums) // [10 20 30 0 0 0 0 0 0 0]
nums = [10]int{0: 10, 9: 20}
fmt.Println(nums) // [10 0 0 0 0 0 0 0 0 20] 通过索引给定义的位置赋值
nums = [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
fmt.Println(nums) // 不定义数组长度,通过值进行推导
nums02 := [10]int{10, 20, 30} // 简短声明数组方式
fmt.Println(nums02) // [10 20 30 0 0 0 0 0 0 0]
}
- 操作
func main() {
// 操作
// 判断是否相等
nums05 := [3]int{1, 3, 4}
nums06 := [3]int{2, 3, 5}
fmt.Println(nums05 == nums06) // false
// 获取数组长度
fmt.Println(len(nums05)) // 3
// 数组元素取值,并且修改数组元素
fmt.Println(nums05[0], nums05[2]) // 1 4
nums05[0] = 2
fmt.Println(nums05) // [2 3 4]
// 遍历数组中的元素
// 方式一:
for i := 0; i < len(nums05); i++ {
fmt.Println(nums05[i])
}
// 方式二:
for index, value := range nums05 {
fmt.Println(index, ":", value)
}
// 切片
// 数组切片的结果不是属组类型,而是切片
fmt.Printf("%T", nums05[0:2]) //[]int
// 多维数组
var marrays [3][2]int
fmt.Println(marrays) // [[0 0] [0 0] [0 0]]
fmt.Println(marrays[0]) // [0 0]
fmt.Println(marrays[0][0]) // 0
marrays[0] = [2]int{1, 3}
fmt.Println(marrays) // [[1 3] [0 0] [0 0]]
// 多维数组字面量
marrays = [3][2]int{{1, 2}, {3, 4}}
fmt.Println(marrays) // [[1 2] [3 4] [0 0]]
}
切片
定义:
- 长度可变的数组,数据类型相同的数据项
- 三部分组成:指针,长度,容量
声明:
- 需要定义组成元素类型,不需要指定存储元素长度
- 切片声明后,会被初始化成nil,表示暂不存在的切片
func main() {
var nums []int
fmt.Printf("%T", nums) // []int
}
- go操作
func main() {
var nums []int
fmt.Printf("%T\n", nums) // []int
// 字面量
nums = []int{1, 2, 3}
fmt.Println(nums) // [1 2 3]
nums = []int{1, 2, 3, 4, 5}
fmt.Println(nums) // [1 2 3 4 5]
fmt.Printf("%#v\n", nums) // []int{1, 2, 3, 4, 5}
// 数组切片赋值
var arrarys [10]int = [10]int{1, 2, 3, 4, 5, 6}
nums = arrarys[1:10]
fmt.Println(nums) // [2 3 4 5 6 0 0 0 0]
fmt.Printf("%#v\n", nums) // []int{2, 3, 4, 5, 6, 0, 0, 0, 0}
// 切片长度和容量获取
fmt.Printf("%#v %d %d\n", nums, len(nums), cap(nums)) // []int{2, 3, 4, 5, 6, 0, 0, 0, 0} 9 9
// make函数
// 设置存储长度为3,容量不指定
nums = make([]int, 3)
fmt.Printf("%#v %d %d\n", nums, len(nums), cap(nums)) // []int{0, 0, 0} 3 3
// 切片指定长度和容量,存储数据根据长度定,不会将容量塞满
nums = make([]int, 3, 5)
fmt.Printf("%#v %d %d\n", nums, len(nums), cap(nums)) // []int{0, 0, 0} 3 5
// 元素操作(增删改查)
fmt.Println(nums[0]) // 0
fmt.Println(nums[1])
fmt.Println(nums[2])
nums[0] = 10
fmt.Println(nums[0]) // 10
// 新增元素,新增后的结果返回值获取
// 新增元素之所以需要被返回值接收?
// 新增元素指针会变
nums = append(nums, 1) // 切片元素最后新增1
fmt.Printf("%#v %d %d\n", nums, len(nums), cap(nums)) // []int{10, 0, 0, 1} 4 5
// 遍历切片中的元素
// 方式一:
for i := 0; i < len(nums); i++ {
fmt.Println(i, nums[i])
}
// 方式二:
for index, value := range nums {
fmt.Println(index, value)
}
// 切片操作
fmt.Printf("%T\n", nums[1:3])
n := nums[1:3:4]
fmt.Printf("%T %#v %d %d\n", n, n, len(n), cap(n)) // []int []int{0, 0} 2 3
// 切片容量副作用
nums = make([]int, 3, 5)
nums02 := nums[1:3]
fmt.Println(nums, nums02) // [0 0 0] [0 0]
nums02[1] = 10
fmt.Println(nums, nums02) // [0 0 10] [0 10] 元素的修改会影响到原
// 删除
// copy实现删除机制
nums04 := []int{1, 2, 3}
nums05 := []int{10, 20, 30, 40}
copy(nums05, nums04)
fmt.Println(nums05) // [1 2 3 40]
copy(nums04, nums05)
fmt.Println(nums04) // [1 2 3]
// 删除索引为0的元素,删除最后一位
num06 := []int{1, 2, 3, 4, 5}
fmt.Println(num06[1:])
fmt.Println(num06[:len(num06)-1])
// 删除中间的元素
copy(num06[2:], num06[3:])
fmt.Println(num06[:len(num06)-1])
// 堆栈:先进后出
// 队列:先进先出
queue := []int{}
queue = append(queue, 1)
queue = append(queue, 2)
queue = append(queue, 3)
queue = append(queue, 4)
// 1,2,3,4
fmt.Println(queue[0])
queue = queue[1:]
fmt.Println(queue) // [2 3 4]
queue = queue[1:]
fmt.Println(queue) // [3 4]
// 堆栈
stack := []int{}
stack = append(stack, 1)
stack = append(stack, 2)
stack = append(stack, 3)
stack = stack[:len(stack)-1]
stack = stack[:len(stack)-1]
fmt.Println(stack) // 1
// 切片排序
nums01 := []int{1, 5, 6, 4, 3}
sort.Ints(nums01)
fmt.Println(nums01) // [1 3 4 5 6]
}
- 多维切片
func main() {
// 多维切片
// 实现切片元素长度不一致的方式
points := [][]int{}
points02 := make([][]int, 0)
fmt.Printf("%T\n", points02)
points = append(points, []int{1, 2, 3})
points = append(points, []int{3, 4, 0})
points = append(points, []int{3, 4, 0, 2, 4, 5})
fmt.Println(points) // [[1 2 3] [3 4 0] [3 4 0 2 4 5]]
// 多维切片访问
fmt.Println(points[0][1]) // 2
}
字节切片函数
var bytes01 []byte = []byte{'a', 'b', 'c'} // 定义字节类型的切片
fmt.Printf("%T %#v\n", bytes01, bytes01) // []uint8 []byte{0x61, 0x62, 0x63}
s := string(bytes01) // 将字节切片转换为字符串
fmt.Printf("%T %v", s, s) // string abc
bs := []byte(s) // 将字符串转换为字节切片
fmt.Printf("%T %v", bs, bs) // string abc[]uint8 [97 98 99]
fmt.Println(bytes.Compare([]byte("abc"), []byte("ccc"))) // -1 对比
fmt.Println(bytes.Index([]byte("abcdefabc"), []byte("dev"))) // -1 寻找索引位置
fmt.Println(bytes.Contains([]byte("abdsdflkj"), []byte("bbbb"))) // false 是否包含
切片与数组
区别:
- 数组是固定长度,切片是可变长长度
- 数组创建变量赋值为老变量,会对值拷贝一份新的
- 切片创建变量赋值为老变量,不会值拷贝,而是直接引用老变量的值。
// 数组和切片区别
// 数组是值类型,新拷贝一份值
// 切片的新的变量会对老变量值做相同修改
slice := []int{1, 2, 3}
slice02 := slice
slice02[0] = 10
fmt.Println(slice, slice02) // [10 2 3] [10 2 3]
// 数组赋值
array01 := [3]int{1, 2, 3}
array02 := array01
array02[0] = 10
fmt.Println(array01, array02) // [1 2 3] [10 2 3]
映射
映射是存储一系列无序的key/value,通过key来对value进行查找
映射的key只能为可使用==运算符的值类型(字符串,数字,布尔,数组),value可以为任意类型。
func main() {
// strint为key类型,int为值类型
var scores map[string]int // nil映射
fmt.Printf("%T %#v\n", scores, scores) // map[string]int map[string]int(nil)
fmt.Println(scores == nil) // true
}
- 字面量&操作
// 初始化
// 字面量
scores = map[string]int{"汤采玉": 100}
fmt.Println(scores) // map[汤采玉:100]
// make函数初始化
scores = make(map[string]int)
fmt.Println(scores) // map[]
// 操作(增删改查)
// key
scores = map[string]int{"汤采玉": 100}
fmt.Println(scores["汤采玉"]) // 100
fmt.Println(scores["不存在的key"]) // 当key不存在返回value的0值
v, ok := scores["汤采玉"] // 判断key值是否存在
fmt.Println(v, ok) // 存在返回value和true,否则为0和false
v, ok = scores["汤文广"] // 因为key不存在
fmt.Println(v, ok) // 0 false
if ok {
fmt.Println(v) // 判断key存在就打印
}
fmt.Println(len(scores))
// if支持初始化,将key返回值写入if中
if v, ok = scores["汤采玉"]; ok {
fmt.Println(v) // 100
}
// 修改,key存在即修改,不存在就是添加
scores["汤采玉"] = 101
fmt.Println(scores) // map[汤采玉:101]
scores["汤文广"] = 150
fmt.Println(scores) // map[汤文广:150 汤采玉:101]
// 删除
delete(scores, "汤文广")
fmt.Println(scores) // map[汤采玉:101]
for k, v := range scores {
fmt.Println(k, v) // 汤采玉 101 遍历,和添加的顺序无关
}
- map嵌套 & 小练习
// key为字符串,value也是一个map
// 名字 = map[字符串]字符串{"地方","联系方式","成绩"}
var users map[string]map[string]string
users = map[string]map[string]string{"烟灰": {"地方": "福建", "联系方式": "110", "成绩": "100"}}
fmt.Println(users) // map[烟灰:map[地方:福建 成绩:100 联系方式:110]]
// 统计投票数量练习:
// 方法一:
abc := []string{"汤采玉", "汤文广", "汤采玉", "汤采玉", "汤红波", "刘利纯", "汤采玉", "汤文广"}
sorts := map[string]int{}
for _, e := range abc {
v, ok := sorts[e]
if ok {
sorts[e] = v + 1
} else {
sorts[e] = 1
}
}
fmt.Printf("%#v\n", sorts)
// 方法二:(代码简化)
aaa := []string{"汤采玉", "汤文广", "汤采玉", "汤采玉", "汤红波", "刘利纯", "汤采玉", "汤文广"}
sortx := map[string]int{}
for _, a := range aaa {
sortx[a]++
}
fmt.Println(sortx)