# 1.6 数据类型:字典与布尔类型
## 1. 字典
字典(Map 类型),是由若干个 `key:value` 这样的键值对映射组合在一起的数据结构。
它是哈希表的一个实现,这就要求它的每个映射里的key,都是唯一的,可以使用 `==` 和 `!=` 来进行判等操作,换句话说就是key必须是可哈希的。
什么叫可哈希的?简单来说,一个不可变对象,都可以用一个哈希值来唯一表示,这样的不可变对象,比如字符串类型的对象(可以说除了切片、 字典,函数之外的其他内建类型都算)。
意思就是,你的 key 不能是切片,不能是字典,不能是函数。。
字典由key和value组成,它们各自有各自的类型。
在声明字典时,必须指定好你的key和value是什么类型的,然后使用 map 关键字来告诉Go这是一个字典。
```go
map[KEY_TYPE]VALUE_TYPE
```
### 声明初始化字典
三种声明并初始化字典的方法
```go
// 第一种方法
var scores map[string]int = map[string]int{"english": 80, "chinese": 85}
// 第二种方法
scores := map[string]int{"english": 80, "chinese": 85}
// 第三种方法
scores := make(map[string]int)
scores["english"] = 80
scores["chinese"] = 85
```
要注意的是,第一种方法如果拆分成多步(声明、初始化、再赋值),和其他两种有很大的不一样了,相对会比较麻烦。
```go
import "fmt"
func main() {
// 声明一个名为 score 的字典
var scores map[string]int
// 未初始化的 score 的零值为nil,无法直接进行赋值
if scores == nil {
// 需要使用 make 函数先对其初始化
scores = make(map[string]int)
}
// 经过初始化后,就可以直接赋值
scores["chinese"] = 90
fmt.Println(scores)
}
```
### **字典的相关操作**
添加元素
```go
scores["math"] = 95
```
更新元素,若key已存在,则直接更新value
```go
scores["math"] = 100
```
读取元素,直接使用 `[key]` 即可 ,如果 key 不存在,也不报错,会返回其value-type 的零值。
```go
fmt.Println(scores["math"])
```
删除元素,使用 delete 函数,如果 key 不存在,delete 函数会静默处理,不会报错。
```go
delete(scores, "math")
```
当访问一个不存在的key时,并不会直接报错,而是会返回这个 value 的零值,如果 value的类型是int,就返回0。
```go
package main
import "fmt"
func main() {
scores := make(map[string]int)
fmt.Println(scores["english"]) // 输出 0
}
```
### 判断 key 是否存在
当key不存在,会返回value-type的零值 ,所以你不能通过返回的结果是否是零值来判断对应的 key 是否存在,因为 key 对应的 value 值可能恰好就是零值。
其实字典的下标读取可以返回两个值,使用第二个返回值都表示对应的 key 是否存在,若存在ok为true,若不存在,则ok为false
```go
import "fmt"
func main() {
scores := map[string]int{"english": 80, "chinese": 85}
math, ok := scores["math"]
if ok {
fmt.Printf("math 的值是: %d", math)
} else {
fmt.Println("math 不存在")
}
}
```
我们将上面的代码再优化一下
```go
import "fmt"
func main() {
scores := map[string]int{"english": 80, "chinese": 85}
if math, ok := scores["math"]; ok {
fmt.Printf("math 的值是: %d", math)
} else {
fmt.Println("math 不存在")
}
}
```
### **如何对字典进行循环**
Go 语言中没有提供类似 Python 的 keys() 和 values() 这样方便的函数,想要获取,你得自己循环。
循环还分三种
1. 获取 key 和 value
```go
import "fmt"
func main() {
scores := map[string]int{"english": 80, "chinese": 85}
for subject, score := range scores {
fmt.Printf("key: %s, value: %d\n", subject, score)
}
}
```
2. 只获取key,这里注意不用占用符。
```go
import "fmt"
func main() {
scores := map[string]int{"english": 80, "chinese": 85}
for subject := range scores {
fmt.Printf("key: %s\n", subject)
}
}
```
3. 只获取 value,用一个占位符替代。
```go
import "fmt"
func main() {
scores := map[string]int{"english": 80, "chinese": 85}
for _, score := range scores {
fmt.Printf("value: %d\n", score)
}
}
```
## 2. 布尔类型
关于布尔值,无非就两个值:true 和 false。只是这两个值,在不同的语言里可能不同。
在 Python 中,真值用 True 表示,与 1 相等,假值用 False 表示,与 0 相等
```python
>>> True == 1
True
>>> False == 0
True
>>>
```
而在 Go 中,真值用 true 表示,不但不与 1 相等,并且更加严格,不同类型无法进行比较,而假值用 false 表示,同样与 0 无法比较。
如下图所示,Goland 直接波浪线提示类型不匹配,不能比较。

Go 中确实不如 Python 那样灵活,bool 与 int 不能直接转换,如果要转换,需要你自己实现函数。
**bool 转 int**
```go
func bool2int(b bool) int {
if b {
return 1
}
return 0
}
```
**int 转 bool**
```go
func int2bool(i int) bool {
return i != 0
}
```
在 Python 中使用 not 对逻辑值取反,而 Go 中使用 `!` 符号
```go
import "fmt"
var male bool = true
func main() {
fmt.Println( !male == false)
// 或者
fmt.Println( male != false)
}
// output: true
```
一个 if 判断语句,有可能不只一个判断条件,在 Python 中是使用 `and` 和 `or` 来执行逻辑运算
```python
>>> age = 15
>>> gender = "male"
>>>
>>> gender == "male" and age >18
False
```
而在 Go 语言中,则使用 `&&` 表示`且`,用 `||` 表示`或`,并且有短路行为(即左边表达式已经可以确认整个表达式的值,那么右边将不会再被求值。
```go
import "fmt"
var age int = 15
var gender string = "male"
func main() {
// && 两边的表达式都会执行
fmt.Println( age > 18 && gender == "male")
// gender == "male" 并不会执行
fmt.Println( age < 18 || gender == "male")
}
// output: false
// output: true
```
---
- 第一章:基础知识
- 1.1 一文搞定开发环境的搭建
- 1.2 五种变量创建的方法
- 1.3 数据类型:整型与浮点型
- 1.4 数据类型:byte、rune与字符串
- 1.5 数据类型:数组与切片
- 1.6 数据类型:字典与布尔类型
- 1.7 数据类型:指针
- 1.8 流程控制:if-else
- 1.9 流程控制:switch-case
- 1.10 流程控制:for 循环
- 1.11 流程控制:goto 无条件跳转
- 1.12 流程控制:defer 延迟语句
- 1.13 流程控制:理解 select 用法
- 1.14 异常机制:panic 和 recover
- 1.15 语法规则:理解语句块与作用域
- 第二章:面向对象
- 2.1 面向对象:结构体与继承
- 2.2 面向对象:接口与多态
- 2.3 面向对象:结构体里的 Tag 用法
- 2.4 学习接口:详解类型断言
- 2.5 学习接口:Go 语言中的空接口
- 2.6 学习接口:接口的三个"潜规则"
- 2.7 学习反射:反射三定律
- 2.8 学习反射:全面学习反射的函数
- 2.9 详细图解:静态类型与动态类型
- 2.10 关键字:make 和 new 的区别?
- 第三章:项目管理
- 3.1 依赖管理:包导入很重要的 8 个知识点
- 3.2 依赖管理:超详细解读 Go Modules 应用
- 3.3 开源发布:如何开源自己写的包给别人用?
- 3.4 代码规范:Go语言中编码规范
- 第四章:并发编程
- 4.1 学习 Go 函数:理解 Go 里的函数
- 4.2 学习 Go 协程:goroutine
- 4.3 学习 Go 协程:详解信道/通道
- 4.4 学习 Go 协程:WaitGroup
- 4.5 学习 Go 协程:互斥锁和读写锁
- 4.7 学习 Go 协程: 信道死锁经典错误案例
- 4.7 学习 Go 协程:如何实现一个协程池?
- 4.8 理解 Go 语言中的 Context
- 4.9 学习一些常见的并发模型
- 第五章:学标准库
- 5.1 fmt.Printf 方法详解
- 5.2 os/exec 执行命令的五种姿势
- 第六章:开发技能
- 6.1 Go 命令:go test 工具详解
- 6.2 单元测试:如何进行单元测试?
- 6.3 调试技巧:使用 GDB 调试 Go 程序
- 6.4 Go 命令: Go 命令指南
- 第七章:暂未分类
- 7.1 20 个学习 Go 语言的精品网站
