1.map
map 是引用类型的,如果声明没有初始化值,默认是nil。空的切片是可以直接使用的,因为他有对应的底层数组,空的map不能直接使用。需要先make之后才能使用。
//1, 声明map 默认值是nil
var m1 map[key_data_type]value_data_type
声明 变量名称 map[key的数据类型]value的数据类型
//2,使用make声明
m2:=make(map[key_data_type]value_data_type)
//3,直接声明并初始化赋值map方法
m3:=map[string]int{"语文":89,"数学":23,"英语":90}
1.1 map 使用
- 插入以及更新语法:map[key]=value
- 删除map中key对应的键值对数据 语法: delete(map, key)
- 访问语法 map[key]
//遍历map
for key, val := range map1 {
fmt.Println(key, val)
}
1.2 map底层原理
源代码解析
struct Hmap
{
uint8 B; // 可以容纳2^B个项
uint16 bucketsize; // 每个桶的大小
byte *buckets; // 2^B个Buckets的数组
byte *oldbuckets; // 前一个buckets,只有当正在扩容时才不为空
};
struct Bucket
{
uint8 tophash[BUCKETSIZE]; // hash值的高8位....低位从bucket的array定位到bucket
Bucket *overflow; // 溢出桶链表,如果有
byte data[1]; // BUCKETSIZE keys followed by BUCKETSIZE values
};
// BUCKETSIZE是用宏定义的8,每个bucket中存放最多8个key/value对, 如果多于8个,那么会申请一个新的bucket,overflow指向它
- Bucket中key/value的放置顺序,是将keys放在一起,values放在一起。
- 扩容使用的是增量扩容:扩容会建立一个大小是原来2倍的新的表,将旧的bucket搬到新的表中之后,并不会将旧的bucket从oldbucket中删除,而是加上一个已删除的标记。当hash表扩容之后,需要将那些旧的pair重新哈希到新的table上,这个工作是逐步的完成(在insert和remove时每次搬移1-2个pair)
查找过程
- 根据key计算出hash值。
- 如果存在old table, 首先在old table中查找,如果找到的bucket已经evacuated,转到步骤3。 反之,返回其对应的value。
- 在new table中查找对应的value。
插入过程分析 - 根据key算出hash值,进而得出对应的bucket。
- 如果bucket在old table中,将其重新散列到new table中。
- 在bucket中,查找空闲的位置,如果已经存在需要插入的key,更新其对应的value。
- 根据table中元素的个数,判断是否grow table。
- 如果对应的bucket已经full,重新申请新的bucket作为overbucket。
将key/value pair插入到bucket中。
2.流程控制
- 我们的程序的执行正常是由上到下逐行执行,叫做 顺序结构 。
- 程序中为了满足某种条件的时候才会执行的结构,叫做选择结构(if、 switch)。
- 当满足条件时候循环反复执行多次的代码, 叫做 循环结构(for)
2.1 if语句
if 布尔表达式{
//布尔条件为true时候执行代码
}
if 布尔表达式 {
//条件成立执行代码
}else{
//条件不成立执行代码
}
if 布尔表达式 {
//布尔条件为true时候执行代码
}else if 布尔表达式2{
//布尔条件为flase时候执行代码
}else{
//上面都不成立执行代码
}
特殊写法:Go语言中可以在if之后,条件判断之间再加上一段执行语句,执行的结果再用作后面的条件判断。(先执行语句,再判断条件)
if a :=1; a==1
{
}
2.2 switch分支语句
if 中判断条件只能为bool类型,但是switch中条件可以为其他类型,case的值必须是唯一的,因为只能进入一个case语句块。
switch 中 break 和fallthrough
break 直接跳出整个switch语句块
fallthrough 当前case执行完了,继续执行下面的case
2.3 for 循环语句
语法: for init; condition;post{ }
init 初始化 只执行一次
condition bool类型 执行条件 如果满足继续执行后面的循环体 如果不满足 则终止执行
{} 循环体
post 表达式 将在循环体执行结束之后执行
break:跳出当前循环,如果后面break后面带标签,则跳到标签处
continue: 中止当前循环,进入下一循环
goto: 和break加标签用法一样
3. 值传递与引用传递
golang中的变量可以分为以下两种类型
- 值类型:int、float、string、bool、array、struct
- 引用类型: slice、pointer、map、chan 等
值类型传递的是数值本身,被修改不会影响原来的值。
引用类型传递的是内存地址,被修改时会影响原来的值。
4. 深拷贝和浅拷贝
浅拷贝复制过来的是内存地址,操作的是同一对象,但是深拷贝是新建一个对象,并且将源对象的内容通通复制一份。
使用深拷贝数据函数: copy(目标切片,数据源)