初探Golang(4)-map和流程控制语句 | 小册免费学

191 阅读4分钟

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)

查找过程

  1. 根据key计算出hash值。
  2. 如果存在old table, 首先在old table中查找,如果找到的bucket已经evacuated,转到步骤3。 反之,返回其对应的value。
  3. 在new table中查找对应的value。 插入过程分析
  4. 根据key算出hash值,进而得出对应的bucket。
  5. 如果bucket在old table中,将其重新散列到new table中。
  6. 在bucket中,查找空闲的位置,如果已经存在需要插入的key,更新其对应的value。
  7. 根据table中元素的个数,判断是否grow table。
  8. 如果对应的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(目标切片,数据源)