golang 数据类型

内置类型

定义在builtin/builtin.go

值类型

    bool
    int  int8, int16, int32(rune), int64
    uint uint8(byte), uint16, uint32, uint64
    float32, float64
    string
    complex64, complex128
    array  //固定长度的数组
    struct //结构体

内置常量

   true   //bool
   false  
   iota   //int 初始0
    nil   //nil类型值

引用类型

    slice    //切片
    map      //哈希映射
    chan     //通道


   // 特殊  零值 nil
    function  //函数
    pointer   //指针  
    interface //接口

内置函数

    append          // 用来追加元素到数组、slice中,返回修改后的数组、slice
    close           // 主要用来关闭channel
    delete          // 从map中删除key对应的value
    panic           // 停止常规的goroutine  (panic和recover:用来做错误处理)
    recover         // 允许程序定义goroutine的panic动作
    real            // 返回complex的实部   (complex、real imag:用于创建和操作复数)
    imag            // 返回complex的虚部
    make            // 用来分配内存,返回Type本身(只能应用于slice, map, channel)
    new             // 用来分配内存,主要用来分配值类型,比如int、struct。返回指向Type的指针
    cap             // capacity是容量的意思,用于返回某个类型的最大容量(只能用于切片和 map)
    copy            // 用于复制和连接slice,返回复制的数目
    len             // 来求长度,比如string、array、slice、map、channel ,返回长度
    printprintln  // 底层打印函数,在部署环境中建议使用 fmt 包

内置接口

//error接口
type error interface {
    Error() string
}

数据类型

基本类型

整型

类型描述
uint8无符号 8 位整型 (0 到 255)
uint16无符号 16 位整型 (0 到 65535)
uint32无符号 32 位整型 (0 到 4294967295)
uint64无符号 64 位整型 (0 到 18446744073709551615)
int8有符号 8 位整型 (-128 到 127)
int16有符号 16 位整型 (-32768 到 32767)
int32有符号 32 位整型 (-2147483648 到 2147483647)
int64有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)
byteuint8,存储字符时选用
runeint32,表示Unicode码
uint32 或 64 位
int与 uint 一样大小,整型没有声明具体类型时,默认的类型
uintptr无符号整型,用于存放一个指针

浮点型

类型描述
float32单精度,IEEE-754 32位浮点型数,有效bit位23个 浮点最精度小数点后15位
float64双精度,IEEE-754 64位浮点型数,默认的类型 浮点最精度小数点后15位
complex6432 位实数和虚数
complex12864 位实数和虚数

布尔型

  1. 常量true false,默认fasle
  2. 不可与整型计算,不可以强转
package main
 
import (
	"fmt"
	"math"
	"unsafe"
)
func main() {
	//-------------类型和所占字节----------------
	num := 2
	fmt.Printf("num is type %T, size %d\n",num,unsafe.Sizeof(num))
	//------------超范围,循环显示----------------
	var n1 int8 = 1
	var n3 = n1 + 127
	fmt.Println("n3:",n3)
	//-----------不同类型,不能操作-------------
	// var n4  = num + n1 invalid operation
 
	//---------------浮点型----------------------
	var f float64 = 1
	f1 := 1.23
	f2 := .23
	f3 := 1.
	fmt.Println("f:",f,"f1:",f1,"f2:",f2,"f3:",f3)
 
	//-------------科学计数法-----------------
	f4 := 1.23E2
	f5 := 123E-2
	fmt.Println("f4:",f4,"f5:",f5)
 
	//---------------保留位数----------------
	f6 := math.Pi
	fmt.Printf("%.3f\n",f6)
 
	//--------------四舍六入五看尾---------------
	fmt.Printf("3.1249	=>	%0.2f(四舍)\n", 3.1249)
	fmt.Printf("3.12671	=>	%0.2f(六入)\n", 3.12671)
	fmt.Printf("3.1351	=>	%0.2f(五后非零就进一)\n", 3.1351)
	fmt.Printf("3.12501	=>	%0.2f(五后非零就进一)\n", 3.12501)
	fmt.Printf("3.1250	=>	%0.2f(五后为零看奇偶,五前为偶应舍去)\n", 3.1250)
	fmt.Printf("9.8350	=>	%0.2f(五后为零看奇偶,五前为奇要进一)\n", 9.8350)
	fmt.Printf("3.2250	=>	%0.2f(五后为零看奇偶,五前为偶应舍去???)\n", 3.2250)
	fmt.Printf("9.7350	=>	%0.2f(五后为零看奇偶,五前为奇要进一???)\n", 9.7350)
 
	// 布尔
	b := false
	// n3 = n1 + b invaliad operation
	fmt.Printf("b的类型:%T,b的值:%v,b的大小:%d",b,b,unsafe.Sizeof(b))
 
}

字符和字符串

字符
  1. 没有字符类型。Ascii码使用byte,其他适合使用rune(UTF-8),使用’'包裹

    a. 字母一个字节
    b. 汉字三个字节

  2. 转义字符

    转义含义
    \r回车符(返回行首)
    \n换行符(直接跳到下一行的同列位置)
    \t制表符
    单引号
    "双引号
    \反斜杠
字符串
  1. 使用""包裹或者``包裹
  2. 如果只包含ascii码,为byte数组
  3. 如果包含中文,为rune数组。rune由1个或多个byte组成

转译

// 双引号支持转译字符
str3 := "我要换行\n换好啦:)\n"
// 反引号不支持转译字符
str4 := `我想换行\n换不了:(\n`  
// 反引号支持多行
str5 := `line1  
line2
`

切片

fmt.Println("1234567"[:3],"1234567"[3:]) // "123"  "4567"
fmt.Println("我是字符串"[:3],"我是字符串"[3:]) // "我"  "是字符串"

拼接

"a"+"b"
+"c""a"+"b"+
"c"  ☑️

遍历

a:= "我是字符串"
//读取byte	
for i:=0;i<len(a);i=i+3{
		fmt.Print(a[i:i+3])
}

//读取rune 字符
for _,s := range "a"{
		fmt.Printf("%c",s)
}

长度

a:= "我是字符串"
println(len(a))  // 15 byte
println(len([]rune(a)))  // 5 rune

转换

a:= "我是字符串"
b:=[]byte(a)
c:=[]rune(a)
d:=string(b)
e:=string(c)

工具

//常见工具包
strings
bytes
unicode

官网doc

中文

类型转换

参考博客

  1. go不支持隐式转换

  2. 转换表达式

    // 表达式 T(v) 将值 v 转换为类型 T
    // type_name 类型 
    // expression 表达式 
    type_name(expression)
    
原类型目标类型方法备注
整型、浮点型整型、浮点型T(v)大数转小数超范围,溢出;
浮点型转整型,小数丢失;
高精度转低精度,精度丢失
任何类型stringfmt.Sprintf()推荐
boolstrconv.FormatBool(b bool) string根据b的值返回"true"或"false"
int64strconv.FormatInt(i uint64, base int) stringbase 必须在2到36之间,结果中会使用小写字母’a’到’z’表示大于10的数字
uint64strconv.FormatUint(i uint64, base int) string同上且为无符号
float64/32strconv.FormatFloat(f float64, fmt byte, prec, bitSize int) stringfmt表示格式:
‘f’(-ddd.dddd)
‘b’(-ddddp±ddd,指数为二进制)
‘e’(-d.dddde±dd,十进制指数)
‘E’(-d.ddddE±dd,十进制指数)
‘g’(指数很大时用’e’格式,否则’f’格式)
‘G’(指数很大时用’E’格式,否则’f’格式)。
prec控制精度(排除指数部分):
对’f’、‘e’、‘E’,它表示小数点后的数字个数;
对’g’、‘G’,它控制总的数字个数。
如果prec 为-1,则代表使用最少数量的、但又必需的数字来表示f。
bitSize表示f的来源类型(32:float32、64:float64),会据此进行舍入。
intstrconv.Itoa()相当于strcov.ParseInt(s string, 10,0) (i int64, err error)
stringstrconv.ParseBool(str string) (value bool, err error)返回字符串表示的bool值。它接受1、0、t、f、T、F、true、false、True、False、TRUE、FALSE;否则返回错误。
float64strcov.ParseFloat(s string, bitSize int) (f float64, err error)bitSize指定了期望的接收类型,
32是float32(返回值可以不改变精确值的赋值给float32),
64是float64;
返回值err是*NumErr类型的,语法有误的,err.Error=ErrSyntax;
结果超出表示范围的,返回值f为±Inf,err.Error= ErrRange。
int64strcov.ParseInt(s string, base int, bitSize int) (i int64, err error)返回字符串表示的整数值,接受正负号。

base指定进制(2到36),如果base为0,则会从字符串前置判断,"0x"是16进制,"0"是8进制,否则是10进制;

bitSize指定结果必须能无溢出赋值的整数类型,0、8、16、32、64 分别代表 int、int8、int16、int32、int64;返回的err是*NumErr类型的,如果语法有误,err.Error = ErrSyntax;如果结果超出类型范围err.Error = ErrRange。
uint64ParseFloat(s string, bitSize int) (f float64, err error)同上,无符号版本

派生类型

数组 array

  • 值类型,声明时零值填。

  • 固定长度,编译期决定。

    • 类型不一致数组不一致
    • 长度不一致数组不一致。
    • 0~len-1下标访问 ,越界panic
  • 占用内存连续

// cmd/compile/internal/types/type.go
// array结构
type Array struct {
	Elem  *Type // 类型指针
	Bound int64 // 数组长度
}

// cmd/compile/internal/types/type.go
// 创建array
func NewArray(elem *Type, bound int64) *Type {
	if bound < 0 {
		Fatalf("NewArray: invalid bound %v", bound)
	}
	t := New(TARRAY)
	t.Extra = &Array{Elem: elem, Bound: bound}
	t.SetNotInHeap(elem.NotInHeap())
	return t
}

img

//--------------声明---------------
//var variable_name [SIZE]variable_type
var arr [3]int

//--------------初始化---------------
//var variable_name [SIZE]variable_type = [SIZE]variable_type{elem...}
//variable_name:= [SIZE]variable_type{elem...}
//variable_name:= [...]variable_type{elem...}
//variable_name:= [SIZE]variable_type{index:elem ...}
var arr1 [3]int = [3]int{1,2,3}  //无类型推导
var arr2 = [3]int{1:4,0:5,2:6}   //类型推导
arr3 := [...]int{7,8,9}          //长度推导
arr4 := [5]int{1:3,3:5}          //使用索引初始化
arr5 := [...] struct{            
   name string
   age  int
}{
  {"user1",1},
  {"user2",2},
}

//--------------遍历---------------
n1 := len(arr1)
for i:=0;i<n1;i++{              // for
  fmt.Println(i,arr1[i])
}

for index,n := range arr3{     // range
  fmt.Println(index,n)
}

多维数组

//--------------声明---------------
//var variable_name [ROW][COL] variable_type
var arr7 [2][3]int

//--------------初始化---------------
//var variable_name [ROW][COL]variable_type = [...][COL]variable_typevariable_type{{elem...}...}
//variable_name:= [ROW][COL]variable_type{{elem...}...}
var arr8 = [...][2]int{{1,2},{2,3}}
arr9 := [1][2]int{{5,6}}

//--------------遍历---------------
for row:=0;row<len(arr8);row++{
		for col:=0;col<len(arr8[0]);col++{
			fmt.Println(row,col,arr8[row][col])
		}
	}

for row,rows := range arr8 {
		for col,val:= range rows{
			fmt.Println(row,col,arr8[row][col],val)
		}
}

切片 slice

  • 引用类型
  • 长度可以变化,容量随长度变化
  • 底层是数组
// cmd/compile/internal/types/type.go
// 编译时slice 结构体
type Slice struct {
	Elem *Type // element type
}

// reflect/value.go
// 运行时slcie 结构体
type SliceHeader struct {
	Data uintptr   //指向底层数组指针
	Len  int       //切片长度
	Cap  int       //切片容量 
}

// cmd/compile/internal/types/type.go
// 创建切片类型 返回切片指针
func NewSlice(elem *Type) *Type {
	if t := elem.Cache.slice; t != nil {
		if t.Elem() != elem {
			Fatalf("elem mismatch")
		}
		return t
	}

	t := New(TSLICE)
	t.Extra = Slice{Elem: elem}
	elem.Cache.slice = t
	return t
}

// runtime/slice.go
// 初始化切片时,如果切片内存逃逸或者太大会调用此方法分配内存
func makeslice(et *_type, len, cap int) unsafe.Pointer {
	mem, overflow := math.MulUintptr(et.size, uintptr(cap)) //内存空间=切片中元素大小×切片容量
	if overflow || mem > maxAlloc || len < 0 || len > cap {
		mem, overflow := math.MulUintptr(et.size, uintptr(len))
		if overflow || mem > maxAlloc || len < 0 {
			panicmakeslicelen()
		}
		panicmakeslicecap()
	}

	return mallocgc(mem, et, true)
}


img

//--------------声明---------------
//var slicename []type
var slice []int  // slice = nil len=0  cap=0

//--------------初始化---------------
// func make(Type, size ...IntegerType[,capacity]) Type 
// capacity必须大雨size
// size 为切片初始长度
// capactity 为切片容量
// var slicename []type = make([]type,len[,cap])
// slicename := []type{elem...}
// slicename := slice[:end[:max]] start<=index<end  max<=end<=len(slice)  len=end-start cap=max-start
// slicename := slice[start:end[:max]] start<=index<end   max<=end<=len(slice)  len=end-start cap=max-start
// slicename := slice[start:[:max]] start<=index<len(slice) max<=end<=len(slice) len=end-start cap=max-start
slice1 := make([]int,1,2,3,4)  // len=3  cap=4
slice2 := []int{6,7}
slice3 := slice1[1:] 

//--------------遍历---------------
for i:=0;i<len(slice3);i++{
  fmt.Print(slice3[i]," ")
}

for _,v := range slice3{
  fmt.Print(v," ")
}

//--------------len/cap---------------
//获取切片长度
//获取切片容量
len(slice1) //3
cap(slice1) //4

//--------------追加---------------
// func append(slice []Type, elems ...Type) []Type
// slice 被添加的切片
// elems 可变元素
// []Type 新切片可能扩容
slice1=append(slice1,5)  // append 追加当个元素
slice1=append(slice1,slice2...) // append 追加切片

//--------------拷贝---------------
// copy(taget,soure) 长度以len(soure)为准
s1 := []int{1, 2, 3, 4, 5}
fmt.Println( s1) // [1 2 3 4 5]
s2 := make([]int, 10)
fmt.Println(s2) // [0 0 0 0 0 0 0 0 0 0]
copy(s2, s1)
fmt.Println(s1) //[1 2 3 4 5]
fmt.Println(s2) //[1 2 3 4 5 0 0 0 0 0]

拷贝

//func copy(dst, src []Type) int
//内置函数copy编译时被改写如下
//cmd/compile/internal/gc/walk.go
/*
// Lower copy(a, b) to a memmove call or a runtime call.
//
// init {
//   n := len(a)
//   if n > len(b) { n = len(b) }
//   if a.ptr != b.ptr { memmove(a.ptr, b.ptr, n*sizeof(elem(a))) }
// }
// n;
//
// Also works if b is a string.
//
*/
func copyany(n *Node, init *Nodes, runtimecall bool) *Node {

  //如果有指针调用 runtime/mbarrier.go typedslicecopy 拷贝
  if n.Left.Type.Elem().HasPointers() {
	  ...
		fn := writebarrierfn("typedslicecopy", n.Left.Type.Elem(), n.Right.Type.Elem())
		...
		return mkcall1(fn, n.Type, init, typename(n.Left.Type.Elem()), ptrL, lenL, ptrR, lenR)
	}
  
  //如果时运行时调用 runtime/slice.go typedslicecopy 拷贝
  if runtimecall {
    ...
		fn := syslook("slicecopy")
		fn = substArgTypes(fn, ptrL.Type.Elem(), ptrR.Type.Elem())
		return mkcall1(fn, n.Type, init, ptrL, lenL, ptrR, lenR, nodintconst(n.Left.Type.Elem().Width))
	}
  ....
}

// runtime/slice.go
// 无指针类型切片或者string copy到另一个切片
func slicecopy(toPtr unsafe.Pointer, toLen int, fromPtr unsafe.Pointer, fromLen int, width uintptr) int {
	if fromLen == 0 || toLen == 0 {
		return 0
	}

	n := fromLen
	if toLen < n {
		n = toLen
	}

	if width == 0 {
		return n
	}

	size := uintptr(n) * width
  //其他代码
  ....
	if size == 1 { 
		*(*byte)(toPtr) = *(*byte)(fromPtr) // known to be a byte pointer
	} else {
		memmove(toPtr, fromPtr, size)
	}
	return n
}

//runtime/mbarrier.go
//go:nosplit 有指针类型切片copy到另一个切片
func typedslicecopy(typ *_type, dstPtr unsafe.Pointer, dstLen int, srcPtr unsafe.Pointer, srcLen int) int {
	n := dstLen
	if n > srcLen {
		n = srcLen
	}
	if n == 0 {
		return 0
	}
  // 其他代码
  ...
	size := uintptr(n) * typ.size
	if writeBarrier.needed {
		pwsize := size - typ.size + typ.ptrdata
		bulkBarrierPreWrite(uintptr(dstPtr), uintptr(srcPtr), pwsize)
	}
	memmove(dstPtr, srcPtr, size)
	return n
}

追加与扩容

//func append(slice []Type, elems ...Type) []Type
//内置函数append编译时被改写
//cmd/compile/internal/gc/ssa.go
func (s *state) append(n *Node, inplace bool) *ssa.Value {
	// If inplace is false, 不需要赋值变量 "append(s, e1, e2, e3)":
	//
	// ptr, len, cap := s
	// newlen := len + 3
	// if newlen > cap {
	//     ptr, len, cap = growslice(s, newlen)    // 调用切片扩容
	//     newlen = len + 3 // recalculate to avoid a spill
	// }
	// // with write barriers, if needed:
	// *(ptr+len) = e1
	// *(ptr+len+1) = e2
	// *(ptr+len+2) = e3
	// return makeslice(ptr, newlen, cap)
	//
	//
	// If inplace is true, 需要赋值变量 "s = append(s, e1, e2, e3)":
	//
	// a := &s
	// ptr, len, cap := s
	// newlen := len + 3
	// if uint(newlen) > uint(cap) {
	//    newptr, len, newcap = growslice(ptr, len, cap, newlen)  // 调用切片扩容
	//    vardef(a)       // if necessary, advise liveness we are writing a new a
	//    *a.cap = newcap // write before ptr to avoid a spill
	//    *a.ptr = newptr // with write barrier
	// }
	// newlen = len + 3 // recalculate to avoid a spill
	// *a.len = newlen
	// // with write barriers, if needed:
	// *(ptr+len) = e1
	// *(ptr+len+1) = e2
	// *(ptr+len+2) = e3
  ...
}


// runtime/slice.go 
// 扩容核心代码
func growslice(et *_type, old slice, cap int) slice {
	
    //1. 大致确定扩容容量
    newcap := old.cap
    doublecap := newcap + newcap
    if cap > doublecap {
      newcap = cap              //a.如果期望容量大于当前容量的两倍就会使用期望容量
    } else {
      if old.len < 1024 {
        newcap = doublecap      //b.当前切片的长度小于 1024 就会将容量翻倍
      } else {
        for 0 < newcap && newcap < cap {
          newcap += newcap / 4  //c.当前切片的长度大于 1024 就会每次增加 25% 的容量,直到新容量大于期望容量
        }
        if newcap <= 0 {
          newcap = cap
        }
      }
   }   
    
   //2. 根据元素大小做内存对齐
    var overflow bool
    var lenmem, newlenmem, capmem uintptr
    switch {
    case et.size == 1:           // 1
      lenmem = uintptr(old.len)
      newlenmem = uintptr(cap)
      capmem = roundupsize(uintptr(newcap))
      overflow = uintptr(newcap) > maxAlloc
      newcap = int(capmem)
    case et.size == sys.PtrSize: // 8
      lenmem = uintptr(old.len) * sys.PtrSize
      newlenmem = uintptr(cap) * sys.PtrSize
      capmem = roundupsize(uintptr(newcap) * sys.PtrSize)
      overflow = uintptr(newcap) > maxAlloc/sys.PtrSize
      newcap = int(capmem / sys.PtrSize)
    case isPowerOfTwo(et.size):  // 2的倍数
      ...
    default:
      ...
    }
    
    //3. 内存申请,底层数组拷贝
    var p unsafe.Pointer
    if et.ptrdata == 0 {  //指针类型
      p = mallocgc(capmem, nil, false)
      memclrNoHeapPointers(add(p, newlenmem), capmem-newlenmem) //清空超出当前长度的内存
    } else {              //非指针类型
      p = mallocgc(capmem, et, true)
      if lenmem > 0 && writeBarrier.enabled {
        bulkBarrierPreWriteSrcOnly(uintptr(p), uintptr(old.array), lenmem-et.size+et.ptrdata)
      }
    }
    memmove(p, old.array, lenmem) //原数组拷贝到新内存中

	  return slice{p, old.len, newcap}
}

https://www.jianshu.com/p/54be5b08a21c
https://blog.csdn.net/qq_35423190/article/details/112803978
https://www.topgoer.cn/docs/golang/chapter03-11

哈希表 map

  • 引用类型
  • Map 是 hash 表来实现的,一种无序的键值对的集合。
  • key 类似于索引,指向数据的value,**key必须可以使用==运算符来比较,不能重复。**int、布尔、string、或包含前面的struct、数组等
  • map自动扩容
//--------------声明---------------
// var mapname map[key_type]value_type
var m map[string]string



//--------------初始化---------------
// var mapname map[key_type]value_type = make(map[key_type]value_type,[size])
// mapname:=map[key_type]value_type{key:value...}
// func make(Type, size IntegerType) Type 
// Type  map[key_type]value_type
// size  map容量预估
var m1 = make(map[string]string,5)
m2 := map[string]string{"野王":"赵云"}
m3 := make(map[string]string)

//--------------查找---------------
// m[key]返回值和是否存在(bool类型)
val,flag := m2["野王"]
if flag{
	fmt.Println("野王:",val)
}

//--------------修改---------------
//map[key] = value 覆盖方式
m1["上官"] = "言为心声"
m1["婉儿"] = "字为心画"
m2["野王"] = "老虎"
m2["上单"] = "吕布"

//--------------删除---------------
//func delete(m map[Type]Type1, key Type)
delete(m1,"上官")

//--------------清空---------------
// make其他当前
m1 = make(map[string]string,0)

//--------------遍历---------------
// for range
// map无序
for key,value := range m2{
  fmt.Println(key,value)
}

// 遍历keys
for key := range m2{
  fmt.Println(key)
}

源码分析

const(
	// flags
	iterator     = 1 // there may be an iterator using buckets
	oldIterator  = 2 // there may be an iterator using oldbuckets
	hashWriting  = 4 // a goroutine is writing to the map
	sameSizeGrow = 8 // the current map growth is to a new map of the same size
)

// 哈希表结构体
// runtime/map.go
type hmap struct {
  count     int // 哈希表元素个数。len()可获取
	flags     uint8  // 扩容标志位
	B         uint8  // len(buckets) == 2^B
	noverflow uint16 // 溢出的buckets数
	hash0     uint32 // 哈希种子 创建哈希表时确定 提供随机性
 
	buckets    unsafe.Pointer // buckets数组指针
	oldbuckets unsafe.Pointer // 哈希在扩容时用于复制的buckets数组指针,它的大小是当前buckets数组的一半
	nevacuate  uintptr        // 搬迁进度(已经搬迁的buckets数量)
 
	extra *mapextra 
}

type mapextra struct {
	overflow    *[]*bmap  //存储hamp.buckets 所有溢出桶
	oldoverflow *[]*bmap  //存储hamp.oldbuckets 所有溢出桶
	nextOverflow *bmap    //指向下一个overflow的bucket
}

// 哈希表结构体中的 bucket
// runtime/map.go
type bmap struct {
	// tophash[0] < minTopHash为存储 bucket evacuation 状态.
	tophash [bucketCnt]uint8   
  // 因为不支持动态类型,编译时推导内存大小
  // cmd/compile/internal/gc/refect
  // func bmap(t *types.Type) *types.Type {} 
  // topbits  [8]uint8      //哈希高8位  减少全等对比提高效率
  // keys     [8]keytype    //哈希表健
  // elems    [8]elemtype   //哈希表值
  // overflow uintptr       //溢出桶指针
}

// 编译时生成hmap代码
// cmd/compile/internal/gc/refect
func hmap(t *types.Type) *types.Type {
	 //其他代码
  ...
  
	// build a struct:
	// type hmap struct {
	//    count      int
	//    flags      uint8
	//    B          uint8
	//    noverflow  uint16
	//    hash0      uint32
	//    buckets    *bmap
	//    oldbuckets *bmap
	//    nevacuate  uintptr
	//    extra      unsafe.Pointer // *mapextra
	// }
	// must match runtime/map.go:hmap.
	fields := []*types.Field{
		makefield("count", types.Types[TINT]),
		makefield("flags", types.Types[TUINT8]),
		makefield("B", types.Types[TUINT8]),
		makefield("noverflow", types.Types[TUINT16]),
		makefield("hash0", types.Types[TUINT32]), // Used in walk.go for OMAKEMAP.
		makefield("buckets", types.NewPtr(bmap)), // Used in walk.go for OMAKEMAP.
		makefield("oldbuckets", types.NewPtr(bmap)),
		makefield("nevacuate", types.Types[TUINTPTR]),
		makefield("extra", types.Types[TUNSAFEPTR]),
	}
  //其他代码
  ...
	return hmap
}

const(
	bucketCntBits = 3
	bucketCnt     = 1 << bucketCntBits
  minTopHash     = 5 // minimum tophash for a normal filled cell.
)

// 编译时生成bmap代码
// cmd/compile/internal/gc/refect
func bmap(t *types.Type) *types.Type {
	
	arr := types.NewArray(types.Types[TUINT8], BUCKETSIZE)
	field = append(field, makefield("topbits", arr))

	arr = types.NewArray(keytype, BUCKETSIZE)
	arr.SetNoalg(true)
	keys := makefield("keys", arr)
	field = append(field, keys)

	arr = types.NewArray(elemtype, BUCKETSIZE)
	arr.SetNoalg(true)
	elems := makefield("elems", arr)
	field = append(field, elems)

	otyp := types.NewPtr(bucket)
	if !elemtype.HasPointers() && !keytype.HasPointers() {
		otyp = types.Types[TUINTPTR]
	}
	overflow := makefield("overflow", otyp)
	field = append(field, overflow)

	bucket.SetNoalg(true)
	bucket.SetFields(field[:])
  //其他代码
  ...
	return bucket
}

// 初始化编译时
// cmd/compile/internal/gc/sinit.go
func maplit(n *Node, m *Node, init *Nodes) {
	// make the map var
	a := nod(OMAKE, nil, nil)
	a.Esc = n.Esc
	a.List.Set2(typenod(n.Type), nodintconst(int64(n.List.Len())))
	litas(m, a, init)

	entries := n.List.Slice()

	// 其他代码
  ...
  // 元素超过25个使用for循环加入hash表
	if len(entries) > 25 {
		
		tk := types.NewArray(n.Type.Key(), int64(len(entries)))
		te := types.NewArray(n.Type.Elem(), int64(len(entries)))
    // 其他代码
      ...
		// make and initialize static arrays
		vstatk := readonlystaticname(tk)
		vstate := readonlystaticname(te)
    // 其他代码
    ...
		// loop adding structure elements to map
		// for i = 0; i < len(vstatk); i++ {
		//	map[vstatk[i]] = vstate[i]
		// }
    // 其他代码
      ...
		return
	}
	// 元素不超过25转换为所有键值对一次性加入哈希表
	// Build list of var[c] = expr.
	// Use temporaries so that mapassign1 can have addressable key, elem.
  // 其他代码
  ...
}

//运行时初始化
// runtime/map.go
func makemap(t *maptype, hint int, h *hmap) *hmap {
	mem, overflow := math.MulUintptr(uintptr(hint), t.bucket.size)
	if overflow || mem > maxAlloc {
		hint = 0
	}

	// initialize Hmap
	if h == nil {
		h = new(hmap)
	}
	h.hash0 = fastrand() // 获取hash种子

  // hint为计算的最小需要同的数量
	// Find the size parameter B which will hold the requested # of elements.
	// For hint < 0 overLoadFactor returns false since hint < bucketCnt.
	B := uint8(0)
	for overLoadFactor(hint, B) {
		B++
	}
	h.B = B

	// 初始化桶
	if h.B != 0 {
		var nextOverflow *bmap
		h.buckets, nextOverflow = makeBucketArray(t, h.B, nil)
		if nextOverflow != nil {
			h.extra = new(mapextra)
			h.extra.nextOverflow = nextOverflow
		}
	}

	return h
}

// runtime/map.go
// makeBucketArray 根据传入的B创建桶数量并分配连续内存存储数据.
func makeBucketArray(t *maptype, b uint8, dirtyalloc unsafe.Pointer) (buckets unsafe.Pointer, nextOverflow *bmap) {
	base := bucketShift(b)
	nbuckets := base
  // 桶数量小于2^4,溢出桶使用可能较小,避免溢出桶创建
  // 桶数量大于2^4,额外创建2^(b-4)个溢出桶
	if b >= 4 {
		nbuckets += bucketShift(b - 4)
		sz := t.bucket.size * nbuckets
		up := roundupsize(sz)
		if up != sz {
			nbuckets = up / t.bucket.size
		}
	}

	if dirtyalloc == nil {
		buckets = newarray(t.bucket, int(nbuckets))
	} else {
		// dirtyalloc was previously generated by
		// the above newarray(t.bucket, int(nbuckets))
		// but may not be empty.
		buckets = dirtyalloc
		size := t.bucket.size * nbuckets
		if t.bucket.ptrdata != 0 {
			memclrHasPointers(buckets, size)
		} else {
			memclrNoHeapPointers(buckets, size)
		}
	}

	if base != nbuckets {
		// We preallocated some overflow buckets.
		// To keep the overhead of tracking these overflow buckets to a minimum,
		// we use the convention that if a preallocated overflow bucket's overflow
		// pointer is nil, then there are more available by bumping the pointer.
		// We need a safe non-nil pointer for the last overflow bucket; just use buckets.
		nextOverflow = (*bmap)(add(buckets, base*uintptr(t.bucketsize)))
		last := (*bmap)(add(buckets, (nbuckets-1)*uintptr(t.bucketsize)))
		last.setoverflow(t, (*bmap)(buckets))
	}
	return buckets, nextOverflow
}


// 访问
/* 编译时会进行如下中代码生成
v     := hash[key] // => v     := *mapaccess1(maptype, hash, &key)
v, ok := hash[key] // => v, ok := mapaccess2(maptype, hash, &key)
*/
// runtime/map.go
func mapaccess1(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer {
	if raceenabled && h != nil {
		callerpc := getcallerpc()
		pc := funcPC(mapaccess1)
		racereadpc(unsafe.Pointer(h), callerpc, pc)
		raceReadObjectPC(t.key, key, callerpc, pc)
	}
	if msanenabled && h != nil {
		msanread(key, t.key.size)
	}
	if h == nil || h.count == 0 {
		if t.hashMightPanic() {
			t.hasher(key, 0) // see issue 23734
		}
		return unsafe.Pointer(&zeroVal[0])
	}
	if h.flags&hashWriting != 0 {
		throw("concurrent map read and map write")
	}
  
  // 1. 获取当前key hash
	hash := t.hasher(key, uintptr(h.hash0))
	m := bucketMask(h.B)
  // 2. 获取桶序号  hash取后B位表示在那个桶
	b := (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize)))
	if c := h.oldbuckets; c != nil {
		if !h.sameSizeGrow() {
			// There used to be half as many buckets; mask down one more power of two.
			m >>= 1
		}
		oldb := (*bmap)(add(c, (hash&m)*uintptr(t.bucketsize)))
		if !evacuated(oldb) {
			b = oldb
		}
	}
  // 获取高8位hash
	top := tophash(hash)
  // 3.循环依次查找正常桶和溢出桶数据
bucketloop:
	for ; b != nil; b = b.overflow(t) {
		for i := uintptr(0); i < bucketCnt; i++ {
      // 高8位比较(低概率)
			if b.tophash[i] != top {
				if b.tophash[i] == emptyRest {
					break bucketloop
				}
				continue
			}
      // 获取对应k
			k := add(unsafe.Pointer(b), dataOffset+i*uintptr(t.keysize))
			if t.indirectkey() {
				k = *((*unsafe.Pointer)(k))
			}
      // 比较k是否符合(低概率)
			if t.key.equal(key, k) {
				e := add(unsafe.Pointer(b), dataOffset+bucketCnt*uintptr(t.keysize)+i*uintptr(t.elemsize))
				if t.indirectelem() {
					e = *((*unsafe.Pointer)(e))
				}
				return e
			}
		}
	}
	return unsafe.Pointer(&zeroVal[0])
}


// 写入与扩容
// runtime/map.go
// Like mapaccess, but allocates a slot for the key if it is not present in the map.
func mapassign(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer {
	if h == nil {
		panic(plainError("assignment to entry in nil map"))
	}
	if raceenabled {
		callerpc := getcallerpc()
		pc := funcPC(mapassign)
		racewritepc(unsafe.Pointer(h), callerpc, pc)
		raceReadObjectPC(t.key, key, callerpc, pc)
	}
	if msanenabled {
		msanread(key, t.key.size)
	}
	if h.flags&hashWriting != 0 {
		throw("concurrent map writes")
	}
 // 1. 获取当前key hash
	hash := t.hasher(key, uintptr(h.hash0))

	// Set hashWriting after calling t.hasher, since t.hasher may panic,
	// in which case we have not actually done a write.
	h.flags ^= hashWriting

	if h.buckets == nil {
		h.buckets = newobject(t.bucket) // newarray(t.bucket, 1)
	}

again:
	bucket := hash & bucketMask(h.B)
  // 如果在迁移
	if h.growing() {
		growWork(t, h, bucket)
	}
  // 2. 获取桶序号 获取桶序号  hash取后B位表示在那个桶
	b := (*bmap)(add(h.buckets, bucket*uintptr(t.bucketsize)))
  // 获取高8位hash
	top := tophash(hash)

	var inserti *uint8           //目标元素在桶中的索引
	var insertk unsafe.Pointer   //目标元素健
	var elem unsafe.Pointer      //目标元素值
bucketloop:
	for {
		for i := uintptr(0); i < bucketCnt; i++ {
      // 匹配tophash
			if b.tophash[i] != top {
				if isEmpty(b.tophash[i]) && inserti == nil {
					inserti = &b.tophash[i]
					insertk = add(unsafe.Pointer(b), dataOffset+i*uintptr(t.keysize))
					elem = add(unsafe.Pointer(b), dataOffset+bucketCnt*uintptr(t.keysize)+i*uintptr(t.elemsize))
				}
				if b.tophash[i] == emptyRest {
					break bucketloop
				}
				continue
			}
      
			k := add(unsafe.Pointer(b), dataOffset+i*uintptr(t.keysize))
			if t.indirectkey() {
				k = *((*unsafe.Pointer)(k))
			}
      // 匹配key
			if !t.key.equal(key, k) {
				continue
			}
			
			if t.needkeyupdate() {
				typedmemmove(t.key, k, key)
			}
			elem = add(unsafe.Pointer(b), dataOffset+bucketCnt*uintptr(t.keysize)+i*uintptr(t.elemsize))
			goto done
		}
		ovf := b.overflow(t)
		if ovf == nil {
			break
		}
    // 遍历溢出桶
		b = ovf
	}

  
	// 桶溢出逻辑处理

	// 扩容 
  // 1. 装载因子超过 count/bucket>6.5 (key多桶少,B+1 双倍扩容)
  // 2. B<15 bucket=2^b overflow>bucket  B>=15 overflow>2^16 (桶多key分散,等量扩容减少overflow,紧凑key)
	if !h.growing() && (overLoadFactor(h.count+1, h.B) || tooManyOverflowBuckets(h.noverflow, h.B)) {
		hashGrow(t, h)
		goto again 
	}

  // 当前桶满了
	if inserti == nil {
    // 创建或者使用之前的溢出桶保存数据
		newb := h.newoverflow(t, b)
		inserti = &newb.tophash[0]
		insertk = add(unsafe.Pointer(newb), dataOffset)
		elem = add(insertk, bucketCnt*uintptr(t.keysize))
	}

	// store new key/elem at insert position
	if t.indirectkey() {
		kmem := newobject(t.key)
		*(*unsafe.Pointer)(insertk) = kmem
		insertk = kmem
	}
	if t.indirectelem() {
		vmem := newobject(t.elem)
		*(*unsafe.Pointer)(elem) = vmem
	}
	typedmemmove(t.key, insertk, key)
	*inserti = top
	h.count++

done:
  //其他代码
	...
	return elem
}


// 扩容
// runtime/map.go
func hashGrow(t *maptype, h *hmap) {
	bigger := uint8(1)
	if !overLoadFactor(h.count+1, h.B) {
    // 溢出桶过多,等量扩容
		bigger = 0
		h.flags |= sameSizeGrow
	}
	oldbuckets := h.buckets // 保存旧桶引用
	newbuckets, nextOverflow := makeBucketArray(t, h.B+bigger, nil) //创建新桶,溢出桶

  // 标记 h.buckets 将挂在 h.oldbuckets
	flags := h.flags &^ (iterator | oldIterator)
	if h.flags&iterator != 0 {
		flags |= oldIterator
	}
  
	h.B += bigger
	h.flags = flags
	h.oldbuckets = oldbuckets
	h.buckets = newbuckets
	h.nevacuate = 0  // 迁移进度 0 
	h.noverflow = 0  // 溢出桶 0

  // extra更新
  // overflow 指向 oldoverflow
  // overflow 清空
  // nextOverflow 更新
	if h.extra != nil && h.extra.overflow != nil {
		// Promote current overflow buckets to the old generation.
		if h.extra.oldoverflow != nil {
			throw("oldoverflow is not nil")
		}
		h.extra.oldoverflow = h.extra.overflow
		h.extra.overflow = nil
	}
	if nextOverflow != nil {
		if h.extra == nil {
			h.extra = new(mapextra)
		}
		h.extra.nextOverflow = nextOverflow
	}
  
  // the actual copying of the hash table data is done incrementally
	// by growWork() and evacuate().
}

// 搬迁bucket
// 迁移结构体
type evacDst struct {
	b *bmap          // 桶 指针
	i int            // 键值 索引
	k unsafe.Pointer // 健 指针
	e unsafe.Pointer // 值 指针
}

 //迁移数据函数
func evacuate(t *maptype, h *hmap, oldbucket uintptr) {
  // 定位老的bucket数组地址
	b := (*bmap)(add(h.oldbuckets, oldbucket*uintptr(t.bucketsize)))
  // 老的bucket桶数量
	newbit := h.noldbuckets()
  // 判断是否被搬迁过
	if !evacuated(b) {
		// xy contains the x and y (low and high) evacuation destinations.
		var xy [2]evacDst
		x := &xy[0]
		x.b = (*bmap)(add(h.buckets, oldbucket*uintptr(t.bucketsize)))
		x.k = add(unsafe.Pointer(x.b), dataOffset)
		x.e = add(x.k, bucketCnt*uintptr(t.keysize))

		if !h.sameSizeGrow() {
      // 双倍扩容,初始化第二个evacDst扩容
			y := &xy[1]
			y.b = (*bmap)(add(h.buckets, (oldbucket+newbit)*uintptr(t.bucketsize)))
			y.k = add(unsafe.Pointer(y.b), dataOffset)
			y.e = add(y.k, bucketCnt*uintptr(t.keysize))
		}

    // 遍历所有溢出桶
		for ; b != nil; b = b.overflow(t) {
			k := add(unsafe.Pointer(b), dataOffset)
			e := add(k, bucketCnt*uintptr(t.keysize))
			for i := 0; i < bucketCnt; i, k, e = i+1, add(k, uintptr(t.keysize)), add(e, uintptr(t.elemsize)) {
				top := b.tophash[i]
				if isEmpty(top) { //被搬迁过
					b.tophash[i] = evacuatedEmpty
					continue
				}
				if top < minTopHash {
					throw("bad map state")
				}
				k2 := k
				if t.indirectkey() {
					k2 = *((*unsafe.Pointer)(k2))
				}
				var useY uint8
				if !h.sameSizeGrow() {
          //双倍扩容 
          //计算hash  rehash
					hash := t.hasher(k2, uintptr(h.hash0))
					if h.flags&iterator != 0 && !t.reflexivekey() && !t.key.equal(k2, k2) {
            // 1/2概率使用新桶
						useY = top & 1
						top = tophash(hash)
					} else {
            // hash溢出桶全部迁移新桶
						if hash&newbit != 0 {
							useY = 1
						}
					}
				}

				if evacuatedX+1 != evacuatedY || evacuatedX^1 != evacuatedY {
					throw("bad evacuatedN")
				}

				b.tophash[i] = evacuatedX + useY // evacuatedX + 1 == evacuatedY
				dst := &xy[useY]                 // evacuation destination

				if dst.i == bucketCnt {
					dst.b = h.newoverflow(t, dst.b)
					dst.i = 0
					dst.k = add(unsafe.Pointer(dst.b), dataOffset)
					dst.e = add(dst.k, bucketCnt*uintptr(t.keysize))
				}
				dst.b.tophash[dst.i&(bucketCnt-1)] = top // mask dst.i as an optimization, to avoid a bounds check
				if t.indirectkey() {
					*(*unsafe.Pointer)(dst.k) = k2 // copy pointer
				} else {
					typedmemmove(t.key, dst.k, k) // copy elem
				}
				if t.indirectelem() {
					*(*unsafe.Pointer)(dst.e) = *(*unsafe.Pointer)(e)
				} else {
					typedmemmove(t.elem, dst.e, e)
				}
				dst.i++
				dst.k = add(dst.k, uintptr(t.keysize))
				dst.e = add(dst.e, uintptr(t.elemsize))
			}
		}
		// 迁移完成 清理指针
		if h.flags&oldIterator == 0 && t.bucket.ptrdata != 0 {
			b := add(h.oldbuckets, oldbucket*uintptr(t.bucketsize))
			// Preserve b.tophash because the evacuation
			// state is maintained there.
			ptr := add(b, dataOffset)
			n := uintptr(t.bucketsize) - dataOffset
			memclrHasPointers(ptr, n)
		}
	}ß

  // 更新搬迁进度
	if oldbucket == h.nevacuate {
		advanceEvacuationMark(h, t, newbit)
	}
}

loadFactor%overflowbytes/entryhitprobemissprobe
4.002.1320.773.004.00
4.504.0517.303.254.50
5.006.8514.77ß3.505.00
5.5010.5512.943.755.50
6.0015.2711.674.006.00
6.5020.9010.794.256.50
7.0027.1410.154.507.00
  • loadFactor:负载因子
  • %overflow:溢出率,具有溢出桶 overflow buckets 的桶的百分比
  • bytes/entry:每个键值对所的字节数开销
  • hitprobe:查找存在的 key 时,平均需要检索的条目数量
  • missprobe:查找不存在的 key 时,平均需要检索的条目数量

todo

// 总结

http://www.zzvips.com/article/194814.html
https://zhuanlan.zhihu.com/p/412581873

指针类型 pointer

  • 引用类型
  • 取地址符是&
  • 指针保存的是地址,地址里的数据才是真正的值,使用*来获取值

int系列、float系列、bool、string、数组、结构体struct,一般在栈

指针、切片、管道、接口、Map是引用类型,一般在堆,GC回收

声明
//空指针 nil
var ptr *int

//零指针
ptr1 := new(int) 
取指针
// 获取指针 i的地址
i:=1
ptr:=&i
// 获取指针的指针 ptr的地址
ptr2:=&ptr
内存

img

类型判断

任何类型都是实现了interface空接口,interface可以承接任何类型后进行类型断言

comma-ok断言

// value ,ok := interface{}[(container)].(fieldtype)
// value container值
// ok 类型断言是否正确
// fieldtype 断言类型

v,ok:=interface{}("我是string").(string)
println(v,ok)  // "我是string" true

type - switch

/*
// data.(type) 获取类型
switch value := interface{}[(container)].(type) {
  case int:
  case string:
      ...
}
*/
switch value := interface{}("我是string").(type) {
  case int:
  case string:
  	println(value)
}
// string
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mars'Ares

请我喝杯咖啡吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值