转摘本文也请表明出处,示例如下

著作由作者马志国在果壳网的原创,若转载请于彰着处标记出处:http://www.cnblogs.com/mazg/

 数组是由同构的要素构成。结构体是由异构的元素结合。数据和结构体都是有稳定内存大小的数据结构。相比之下,切片和照耀则是动态的数据结构,它们依据需要动态增长。

正文内容是自个儿对Go语言的变量、常量、数组、切片、映射、结构体的备忘录,记录了重要的连锁知识点,以供翻查。

4.1 数组

数组是一多样一样序列数据的成团,数组中带有的每个数据被誉为数组元素。一个数组包含的元素个数称为数组的长短,数老董度是从来的。一个数组可以由零个或两个要素构成。

1 数组注明

数组注脚的相似形式:

var 数组名 [长度]类型

演示如下:

var arr [10]int           //10个元素的整型数组

var ptrs [5]*float64  //5个元素的指针数组,每个指针都指向float64类型 

var points [8]struct{ x, y int }  //8个元素的结构体类型

var arry [2][3]int               //2*3的二维整型数组 

2 简短注明

与变量的简约表明一样,数组也得以概括讲明。即便在数组的长短地方现身的是“…”省略号,则意味着数据的长度是按照初阶化值得个数来计量。

a := [3]int{1, 2, 3} // 长度为3的数组

b := [5]int{1, 2, 3} //长度为10,前三个元素为1、2、3,其它默认为0

c := […]int{4, 5, 6} //长度3的方式,Go自动计算长度

r := […]int{9: 6}    //长度为10,最后一个元素的值为6,其它默认为0

arr2 := [2][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}}//二维数组

3 元素访问

数组的各类元素通过索引下标来访问,内置的len函数将赶回数组中元素的个数。

arr := […]int{1, 2, 3, 4, 5}

len := len(arr)   //len获取数组长度

fmt.Println("修改前:", arr)

arr[0] = 100      //下标访问数组元素

fmt.Println("修改后:", arr)

fmt.Println("数组长度:", len)

打印结果:

修改前: [1 2 3 4 5]

修改后: [100 2 3 4 5]

数组长度: 5

4 数组遍历

二种遍历情势,其中range表明式遍历有几个重返值,第一个是索引,第二个是因素的值。示例如下:

arr := […]int{1, 2, 3, 4, 5}

len := len(arr) //len获取数组长度

for i := 0; i < len; i++ {

  fmt.Println(i, arr[i])

}

for i, v := range arr {

  fmt.Println(i, v)

}

 

5 用作函数参数

在Go语言中,数组作为函数的参数仍然是值传递,尽管能够应用数组的指针来代表,然则改变不了数首席营业官度。这时,我们司空眼惯接纳切片slice来顶替数组。

 

6 数组相比

假使数组元素的系列是可正如的,那么这多少个数组也是可的可比。唯有数组的所有因素都相当数组才是异常的。由于长度也是数组类型的一有些,所以长度不同的数组是见仁见智的。

数组可遍历、可修改,是否可正如,由数组元素决定。%T用于显示一个值对应的数据类型。

 

文中如有错误的地点请我们提议,以免误导!转摘本文也请讲明出处:Go语言备忘录(1):基本数据结构多谢!**

4.2 数组切片(slice)

数组的长度在概念之后无法修改。数组是值类型,每回传递都将发出一份副本。分明这无法满意开发者的少数需求。Go语言提供了数组切片(slice)来弥补数组的供不应求。数组和slice之间有着紧密的交换,一个slice是一个轻量级的数据结构,提供了拜访数组子系列元素的职能,而且slice的底层确实引用一个数组对象。

1 阐明数组切片(slice)

    数组切片与数组表明十分相似,唯一区别是无需点名长度。

var 数组切片 []类型

var slice []int

2 基于数组以及遵照切片创制切片

arr := […]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} //数组

slice1 := arr[0:5]     //基于数组切片1,左闭右开

slice2 := slice1[1:3]  //基于切片1创建切片2

fmt.Println(slice1)    //[1 2 3 4 5]

fmt.Println(slice2)    //[2 3]

3 直接成立切片

Go语言提供的放权函数make()能够用于灵活的创立数组切片。make()函数创制一个指定元素类型、长度和容量的slice。容量部分可以大概,在这种场馆下,容量将相当于长度。在底部,make创制了一个匿名的数组变量,然后回来一个slice。唯有由此再次来到的slice才能引用匿名的数组变量。

make([]T,len)

make([]T,len,cap)

放到的len()函数获取长度,内置的cap()函数获取容量。

slice1 := make([]int, 5)

slice2 := make([]int, 5, 10)

slice3 := []int{1, 2, 3, 4, 5}

fmt.Println(slice1, len(slice1), cap(slice1))

fmt.Println(slice2, len(slice2), cap(slice2))

fmt.Println(slice3, len(slice3), cap(slice3))

打印结果:

[0 0 0 0 0] 5 5

[0 0 0 0 0] 5 10

[1 2 3 4 5] 5 5

与数组相同,slice操作无法超出len指定的限量。

slice := make([]int, 5, 10)

slice[3] = 10 //正确

slice[8] = 8 //错误 ,索引超出范围

 

 

4 切片的遍历

切开的遍历与数组的遍历形式同样。

5 切片不可能相比较

与数组不同的是slice之间不能够比较,因而我们不可以运用==操作符来判断五个slice是否包含全体等于的要素。然而标准库提供了冲天优化的bytes.Equal函数两个字节型slice是否等于,可是对于其他品类的slice,大家必须协调进行每个元素举办相比。

切开可遍历,可修改,不可相比

6 判定切片是否为空

接纳len(s)==0来判断一个slice是否为空。

7 追法郎素

置于的append函数用于向slice追加因素。可以直接增新币素,也足以追加一个slice。注意参数slice后有…。否则有语法错误。因为append()函数的语义是从第二个参数先导都应有是待附加的元素。slice后加…意味将slice的因素全体打散后传出。数组切片会活动处理存储空间欠缺的问题。假若扩张的始末长度领先近年来已分配的存储空间(即cap()调用再次来到的音信),数组切片会自动分配一块充裕大的内存。 

slice := make([]int, 5, 10)

slice = append(slice, 1, 2, 3)

fmt.Println(slice)

slice2 := []int{4, 5, 6}

slice = append(slice, slice2…)

fmt.Println(slice)

打印结果:

[0 0 0 0 0 1 2 3]

[0 0 0 0 0 1 2 3 4 5 6]

 

8 切片复制

内置的copy函数用于数组切片的复制。复制时无需考虑对象数组和源数组的尺寸。

slice1 := []int{1, 2, 3, 4, 5}

slice2 := []int{7, 8, 9}

copy(slice2, slice1) //只会将slice1的前3个元素赋值给slice2

fmt.Println(slice2)

slice3 := []int{1, 2, 3, 4, 5}

slice4 := []int{7, 8, 9}

copy(slice3, slice4) //将slice4的元素赋值slice3的前3个元素

fmt.Println(slice3)

打印结果:

[1 2 3]

[7 8 9 4 5]

9 作为函数参数时切片与数组的区分

func SetValueByArray(arr [5]int) {

    arr[0] = 100

}

 

func SetValueBySlice(slice []int) {

    slice[0] = 100

}

func main() {

    arr := [5]int{1, 2, 3, 4, 5}

    SetValueByArray(arr)

    fmt.Println(arr)  

    slice := arr[:]

    SetValueBySlice(slice)

    fmt.Println(arr)

}

//打印结果:

[1 2 3 4 5]

[100 2 3 4 5]

 

10字符串和byte切片

标准库中提供了4个与字符串操作相关的包:

功能

strings

提供了字符串查询、替换、比较、截断、拆分和合并等功能。

bytes

提供了很多与strings包类似的功能。因为字符串是只读的,逐步构建字符串会导致很多分配和复制,这种情况下,使用bytes.Buffer类型将会更有效。

strconv

提供了布尔类型、整数、浮点数和对应字符串的相互转换,还提供了双引号转义相关的转换。

unicode

提供了IsDigit、IsLetter、IsUpper和IsLower等功能,用于给字符分类。

 

strings包常用的6个函数,效率参考Go中文帮忙文档

func Contains(s, substr string) bool

func Count(s, sep string) int

func Fields(s string) []string

func HasPrefix(s, prefix string) bool

func Index(s, sep string) int

func Join(a []string, sep string) string

 

byte包常用的的6个函数,效能与strings类似。

func Contains(b, subslice []byte) bool

func Count(s, sep []byte) int

func Fields(s []byte) [][]byte

func HasPrefix(s, prefix []byte) bool

func Index(s, sep []byte) int

func Join(s [][]byte, sep []byte) []byte

它们中间的绝无仅有区别就是字符串类型的参数替换成了字节slice类型的参数。

 参考书籍《The Go Programming Language》、《Go In
Action》、
《Go语言学习笔记》等

4.3 映射

辉映是一个无序的键值对聚集,其中有着的键都是不同的,然后通过给定的键可以在常数时间复杂度内搜寻、更新或删除相应的值。在Go语言中,一个map就是一个哈希表的引用,映射中兼有的键都有一致的档次,所有的值也有所一样的档次,不过键和值期间可以是不同的数据类型。

1 表明映射

扬言的一般格式为:

var 映射名称 map[键]值

只是宣称一个map,它的开端值是nil,也就是没有引用任何哈希表。所以不可以向一个nil值的map存入元素。

var ages map[string]int

fmt.Println(age == nil)//”true” 

fmt.Println(len(ages)== 0)//”true” 

  2 开立映射

内置的make函数可以创立一个map,创制后得以存入元素。

myMap1 := make(map[string]int)      //创建一个map

myMap2 := make(map[string]int, 100) //创建一个map,初始储存能力为100

myMap3 := map[string]int{

     "str1": 10, "str2": 20, "str3": 30} //直接创建,并初始化

fmt.Println(myMap1, len(myMap1))

fmt.Println(myMap2, len(myMap2))

fmt.Println(myMap3, len(myMap3))

打印结果:

map[] 0

map[] 0

map[str3:30 str1:10 str2:20] 3

 

3  元素的赋值和删除

  要素得以间接赋值,内置的delete函数用于删除元素,删除一个不设有的因素,不会促成错误。

 myMap3 := map[string]int{"str1": 10, "str2": 20, "str3": 30} 

fmt.Println(myMap3, len(myMap3))

myMap3["str5"] = 50

fmt.Println(myMap3, len(myMap3))

delete(myMap3, "str")

delete(myMap3, "str3")

fmt.Println(myMap3, len(myMap3))

打印结果:

map[str3:30 str1:10 str2:20] 3

map[str5:50 str1:10 str2:20 str3:30] 4

map[str1:10 str2:20 str5:50] 3

 

4 查找元素

有时可能需要知道对应的因素是否在map中,例如,假设元素类型是一个布尔类型,你或许需要区分一个零值的因素,和因为元素不存在而回到的零值,可以像下面这样使用:

v,ok:=map[key]

if !ok{/*在map中不存在key为键的元素*/}

 

//结合起来使用

if v,ok:=map[key];!ok{/*    */ }

在这种情景下,map下标语法将发生多个值;第二个值是一个布尔类型,用于表示元素是否存在。布尔变量一般命名为ok。示例如下:

myMap3 := map[string]int{"str1": 10, "str2": 20, "str3": 30}

if v, ok := myMap3["str2"]; ok {

  fmt.Println(v)

}

 

5 遍历映射

遍历map使用range风格的for循环实现。由于map是无序的集结,所以每一次遍历的逐条可能不均等。

myMap3 := map[string]int{"str1": 10, "str2": 20, "str3": 30}

for k, v := range myMap3 {

   fmt.Println(k, v)

}

6 映射不可相比

和slice一样,map在此之前也无法举办相等相比;唯一的例外是和nil举行比较。要看清六个map是否包含相同的key和value,大家亟须经过一个循环往复实现。有时候,我们需要一个map的key是slice类型,但是map的key必须是可正如的体系,可是slice并不满足那些规格。我们得以因而六个步骤绕过这一个限制。第一步,定义一个扶植函数k,将slice转为map对应的string类型的key,确保唯有x和y相等时,k(x)==k(y)才确立。然后成立一个key为string类型的map,在每便对map操作时,先用k协理函数将slice转化为string类型。

7 不可能对映射元素取址操作

map中的元素并不是一个变量,不可以对map元素进行取址操作。禁止对map元素取址的原由是map可能随着元素数量的进步而重新分配更大的内存空间,从而可能导致前面的地址无效。slice元素可以取址操作。

fmt.Println(&myMap3["str1"])//错误,不能取址操作

8 nil值映射

map上的大部分操作,包括查找、删除、len和range循环都可以高枕无忧工作在nil值的map上,它们的所作所为和一个空map类似。可是向一个nil值的map存入元素将造成一个panic分外。

 

目录:

  1. 变量
  2. 常量
  3. 数组
  4. 切片
  5. 映射
  6. 结构体

一、变量

  •  变量是一段或多段用来存储数据的内存;
  • 变量总是有固定的数据类型,类型决定了所占内存的长度和储存格式;
  • 编译后的代码应用变量的内存地址来做客数据,而不是变量名;
  • 简短变量表明只可以在函数内注脚(局部变量),var讲明格局则无界定(但一般用来注解未显式着手化的变量);
  • 宣称同一功能域中的同名变量时,将回退为赋值,即重用该变量(必须至少有一个新变量定义);
  • 而阐明不同功用域的同名变量则为再度定义(覆盖);

    var q int
    var y = 453
    var (

    n,m = 134,"srf"
    n1,m1 int 
    

    )
    func f1() {

    n,m := 25,"sss" 
    n,m1 := 34,"yyy"
    fmt.Println(n,m,m1)
    n = n+5 //赋值表达式中,首先计算右值
    //“_”空标识符用来临时规避编译器对未使用变量和导入包的错误检查
    if _,ok := add1(n);ok {
        fmt.Println(n)
    }
    

    }
    func add1(n int) (int, bool) {

    return n+1,true
    

    }

  

二、常量、枚举

  • 常量是一个不可改变的值,它可以为字面量,或编译器能推断出结果的表明式。未拔取的常量不会唤起编译错误;
  • 在常量组中如不指定项目和开头值,则与上一行非空常量右值相同;
  • 常量会被编译器在预处理阶段直接举行,作为指令数据采纳,所以不可能取常量的地点;

    const i = 5
    const (

    x byte = 1
    x1
    x2       //x1,x2均为1
    s = "abs"
    s1       //s1=“abc”
    

    )
    const (

    _,_ int = iota,iota*3 //0,0*3 忽略值,并显式指定类型为int
    k1,k2             //1,1*3
    l1,l2             //2,2*3
    o1,o2 = 5,6       //中断iota自增
    r1,r2             //5,6  同上一行
    e1,e2 = iota,iota*3 //5,5*3  恢复iota自增,按行递增
    

    )
    //枚举
    type color byte
    const (

    blue color = iota
    red
    green
    

    )
    func main() {

    t:= blue
    fmt.Println(t) //0
    //fmt.Println(&i) //错误:无法对常量取地址 cannot take the address of i
    

    }

  

三、数组

数组是切片和照耀的根基数据结构。数组是值类型,在赋值和传递数组时将拷贝整个数组。

数组是一个长短固定的数据类型,存储着一段具有相同数据类型元素的总是内存块。

因为数组占用的内存是连接分配的,所以对数组元素的查询、修改等操作速度很快。

扬言数组的艺术:

  • var array1 [5]int
  • array1 := [5]int{3,5,6,3,2}
  • array1 := […]int{3,4,7,8,1}
    //遵照数组字面量中元素的个数来规定数组的尺寸
  • array1 := [5]int{0:3,3:5,4:8}
    //只开头化指定索引的要素,此外元素保持零值
  • array1 := […]int{1,2,9:32}

数组元素的门类可以为另外内置类型,也足以是某种结构类型,也得以是指针类型。

数组变量的类别包括数总经理度和因素的系列,唯有两部分都一致的数组才可互相赋值。

多维数组:数组本身唯有一个维度,只好通过整合三个数组来创制多维数组;内置函数len、cap均重返第一维度的尺寸

  • var array [4][2]int
  • array := [4][2]int{2:{20,21},3:{41,25}}
  • array := [4][2]int{2:{1:21},3:{0:41}}
  • array := […][4]int{{2,3},{4,5}} //仅第一维度允许利用“…”
  • array[2][1] = 10

在函数间传递数组:由于在函数间传递变量时,传递的连接变量的值的副本,因为数组是值类型,所以在赋值和传递数组变量时将拷贝整个数组!在概念函数时,对于较大的数据类型应该把参数设计为指针类型,这样在调用函数时,只需在栈上分配给各种指针8字节的内存,但这意味会变动指针指向的值(共享的内存),其实大部分动静下应该接纳切片类型,而不是数组。

小心:因为切片的底部数组可能会在堆上分配内存,对于小数组在栈上拷贝的耗费可能比make代价小;

四、切片slice

  • 切开slice是引用类型,它其中通过指针引用一个底层数组,并设定相关属性将数据的读写操作限定在指定区域。

    //切片本身是个只读对象,工作体制仿佛数组指针的一种包装
    type slice struct{

    array unsafe.Pointer
    len int //可读写的元素数量
    cap int //所引用数组片段的真实长度
    

    }

创办和开头化:

  • slice1 := make( []string, 5 )
    //创造一个尺寸、容量都为5的string类型的切片
  • slice1 := make( []string, 3, 5 )
    //创设一个尺寸为3,容量为5的string类型的切片
  • slice2 := []string{ “red”,”blue”,”green” } //长度和容量均为3的切片
  • slice2 := []int{ 99:1 }
    //长度和容量均为100,并伊始化第100个元素为1

双重切片reslice:以开头和得了原切片的目录地点确定所引用的数组片段,不扶助反向索引,实际范围是一个右半开区间
如果原切片slice容量为k,新切片newSlice为原切片的索引 i
元素地方上马,在原切片的容量范围内取值

  • newSlice := slice[ i : j ]  //长度为j-i,容量为k-i
  • newSlice := slice[ i : j : n ]
    //限制新切片的容量为n-i(第六个参数n-1表示新切片可扩展到的结尾一个可见的最底层数组部分的元素索引,这样就直达了限定容量的目标,注意:n必须>=j)
  • 新切片不可能访问它所针对的底部数组的率先个要素在此以前的有些(第一个目录在此以前的有些)
  • 例子:ss:=[]int{10,20,30,40,50}       newss:=ss[2:4:5]  
    //newss为[30,40],容量为3
  • 新切片和旧切片指向同一个平底数组;

    //利用reslice实现一个栈式结构(也可将stack定义为一个档次)
    var stack = make([]int,0,5)

    func push(x int) error {

    n:=len(stack)
    if n == cap(stack) {
        return errors.New("stack is full")
    }
    stack = stack[:n+1] //新的stack增加了一个可访问元素stack[n]
    stack[n]=x
    return nil
    

    }
    func pop() (int, error) {

    n:=len(stack)
    if n == 0 {
        return 0,errors.New("stack is empty")
    }
    x:=stack[n-1]
    stack = stack[:n-1] //新的stack减少了一个可访问元素stack[n-1]
    return x,nil
    

    }
    func main() {

    for i := 0; i < 7; i++ {
        fmt.Printf("push %d: %v,%v\n",i,push(i),stack)
    }
    for i := 0; i < 7; i++ {
        x,err:=pop()
        fmt.Printf("push %d: %v,%v\n",x,err,stack)
    }
    

    }

切开的尺寸可以按需自行增长或收缩:

  • 动态增长是通过append()函数实现的
  • 压缩则是因此对它又一次切片来贯彻,通过重新切片拿到的新切片将和原切片共享底层数组,它们的指针指向同一个底层数组。

是因为切片只是援引了底层数组,底层数组的多少并不属于切片本身,所以一个切开只需要24字节的内存(在64位机器上):指针字段8字节、长度字段8字节、容量字段8字节。所以在函数之间一直传送切片是飞速的,只需分配24字节的栈内存。

nil切片和空切片:

  • nil切片:只注解,但未开头化的切片,如var slice1
    []int,nil切片可用来描述一个不存在的切片
  • 空切片:长度和容量均为0的切片,创立空切片时未对底层数组元素分配任何内存,可用来代表空集合,如slice1
    := make( []int, 0 ),slice2 := []int{}
  • 对nil切片和空切片调用内置函数append、len、cap的功用等同

append()函数:
slice = append(slice, elem1, elem2)  //一回可扩大六个值
slice = append(slice, anotherSlice…)
 //使用“…”将anotherSlice的富有因素追加到slice里

  • 当slice还有可用的容量时,append()操作将可用的因素合并到切片的长度,并对其赋值,最终回来一个崭新的切片(和旧切片共享同一个平底数组);
  • 要是slice的容量不足时,append()操作会创设一个新的底部数组,并将被引用的旧值复制到新数组里,然后追加新的值;
  • 原切片容量不足时,且小于1000,则新切片的容量为原容量的2倍,若超越1000,则容量的增长因子变为1.25;
  • 由于容量不足时,append操作会重回一个所有温馨单独的底层数组的新切片,即与原切片不共享同一底层数组,对新切片元素的改动将不会潜移默化原切片的底部数组,技巧:在创设切片时设置长度等于容量,这样就足以强制在率先次append操作时创建新的最底层数组,达到与原数组分离的目标,如newSlice
    := oldSlice[2:3:3]

copy函数:在五个切片对象之间复制数据,允许指向同一个底层数组,允许目的区间重叠。最后所复制长度以较短的切片长度为准

  • 切开的迭代如:for index, value := range slice{ ….
    },index为当前迭代到的目录地点,value是从slice的副本中取值,index和value变量的内存地址是不变的,只是指向的值在不断更新。
  • len函数可返还切片的长度、cap函数可返还切片的容量
  • 多维切片(类似交错数组):切片和数组一样,本身是一维的,可以组合三个切片来形成多维切片,如:slice
    := [][]int{ {12},{34,23}
    },slice[0]为{12},slice[1]为{34,23}
  • 只顾:倘诺切片长日子引用大数组中很小的有些,则应当复制出所需数据,新建独立切片,以便原大数组内存可被立刻回收;

 

五、映射map

映射map:是一个仓储键值对的无序汇聚,它能基于键神速搜索数据,键就像索引一样,指向与该键关联的值;

炫耀是无序的,每一回迭代它时顺序可能不平等,因为映射内部选择了散列表;

照耀的散列表包含一组桶,每个桶里存储着一些键值对;

辉映内部选择了多个数组:

  • 先是个数组:存储着用于拔取桶的散列键的高八位值,该数组用于分别每个键值对要留存哪些桶里;
  • 其次个数组:每个桶里都有一个字节数组,先逐一存储了该桶里的所有键,之后存储了该桶的所有值;

在存储、删除、查找映射的键值对时,会把指定的键传给映射的散列函数,该函数把键转换为一个散列值,然后把该散列值与第一个数组比对来抉择哪个桶,再到桶里的字节数组里搜寻对应的键和值;

创造和开端化映射:

  •     dict1 := make(map[string]int) //空映射,等同于dict1 :=
    map[string]int{}
        dict1 := make(map[string]int, 1000)
    //预先分配充足内存空间,有助于提高性能 
        dict2 := map[string]int{“srf”:143,”robt”:342}
  • 照耀的键:只好是能用“==”做比较的序列,但不得以是切片、函数、以及包含切片的品类,因为他们拥有引用语义。而映射的值则足以是轻易档次;
  • nil映射是只声明而未起先化的照射,不能间接利用,如var dict
    map[string]int。空映射则足以从来运用;
  • map类型的零值是nil,也就是没有引用任何哈希表。在向map存数据前务必先创设map,即:引用一个哈希表。

一经要用map存储大量小目的,应该向来存储为值类型,而非指针类型,有助于减弱需要扫描的对象数量,大幅缩编垃圾回收时间;

从映射中取值:

  • value := dict2[“srf”]
    //键存在时回来对应的值,不设有时回来类型的零值
  • value, ok := dict2[“srf”] //ok为键是否存在的布尔标志
    if ok { …… }
  • map中的元素并不是一个变量,我们不可以对map的因素举办取址操作(因为map可能会在动态增长时重新分配内存),因而不可能直接改动value成员,而应该通过临时变量来修改,或把值定义为指针类型:
m := users[int]user{
    1:{"srf",25}
}
//m[1].age +=1 //错误,无法设置值
u := m[1]
u.age+=1
m[1] = u

遍历映射:

  • for key := range dict2 { ….. } //只接收键
  • for key, value := range dict2 { …… } //同时接收键和值
  • 遍历映射时,可以添加、删除成员
  • 遍历映射的键值对时的各种是随便,若要有序的拿到映射的键值对,则需要先遍历出映射的键存到一个切片中,然后排序该切片,最终遍历该切片,按切片中元素的顺序去炫耀中取对应的值

delete(dict2,”srf”) 从映射中剔除指定的键值对;

运作时会对映射的面世操作做出检测,对映射的操作只可以同步举办(同一时刻只好有一个任务在操作映射),否则会导致进程崩溃。可用读写锁sync.RWMutex实现共同,制止读写操作同时展开:

func main() {
    var lock sync.RWMutex
    m:=make(map[string]int)

    go func() {
        for {
            lock.Lock()
            m["a"] += 1
            lock.Unlock()  //不能用defer

            time.Sleep(time.Microsecond)
        }
    }()

    go func() {
        for {
            lock.RLock()
            _ = m["b"]
            lock.RUnlock()

            time.Sleep(time.Microsecond)
        }
    }()

    select {} //阻止进程退出
}
  • 在函数间传递映射与传递切片一样(无须再次取地址),传递的只是映射本身的副本,而不会复制映射所引用的具备底层数据结构,对该映射副本所做的修改将会映现到具备对那么些映射的引用。
  • 多维映射:即值为照射类型的投射。使用时应小心,作为值的映射也急需起初化后才能使用,如:
        var m1 = make(map[int]map[string]string)
        m1[13]= map[string]string{“srf”:”yes”}
  • 判断五个map是否等于的函数:

    func equal(x, y map[string]int) bool {

    if len(x) != len(y) {
        return false
    }
    for k, xv := range x {
        if yv, ok := y[k]; !ok || yv != xv {
            return false
        }
    }
    return true
    

    }

  • 用map来表示字符串的集合set: 

    m:=make(map[string]bool)
    if !m[“srf”] {

    m["srf"] = true
    

    }

 

六、结构体

  • 布局体struct是一种复合类型,由两个不同类另外命名字段(field)体系打包而成;
  • 字段名必须唯一,可用“_”补位,帮忙选拔我的指针类型成员(这足以让大家创设递归的数据结构,比如链表和树结构等);
type node struct{
    _ int
    id int `账号`
    next *node
}
  • 布局体类型音信包括:字段名、字段标签、排列顺序,唯有三者全体一样才可认为是如出一辙档次;
  • 可按顺序最先化全体字段,但提出拔取命名格局开头化部分或任何字段(可忽视字段的定义顺序,便于结构体成员相继的改动、扩张);
  • 结构体的比较:唯有当结构体的有着成员都是可正如的,那么该结构体才是可正如的
  • 可径直定义一个匿名的社团体类型,并赋值给一个变量,或用作字段的连串(匿名结构体字段不可以用字面量形式直接起初化,需要“.”语法来最先化其成员)

    u := struct{

    name string
    

    }
    type file struct{

    name string
    attr struct{
        owner int
        perm int
    }
    

    }
    f := file{name:”test.dat”}
    f.attr.owner = 1
    f.attr.perm = 0755

  • 空结构:struct{},长度为0,不分配内存,它和其他具有“长度”为0的靶子平时都指向runtime.zerobase变量(即它们都针对同一个变量);空结构类型平时作为通道元素的序列,用于事件通报(优点是不占内存);

匿名字段(嵌入类型):即没有点名显式的名称,只有项目标字段:

  • 编译器将隐式地以项目名作为字段名称(不含有包名);
  • 外层的结构体不仅取得了匿名成员类型的保有成员,而且也赢得了该品种全体的导出的方法;
  • 可一向引用嵌入类型字段的成员,但在以字面量语法起先化时须显式起始化它的总体结构;
  • 匿名字段的分子的数据类型必须是命名的门类或针对一个命名的项目标指针,不可以是接口指针和多重指针;
  • 无法将基础项目和其指针类型同时作为匿名字段
  • 字段重名处理:优先使用外层字段(内层字段被屏蔽了,只好通过一点一滴限定名来访问),对于四个一律层级的同名字段也必须通过一点一滴限定名来访问,否则编译器无法确定目的;

字段标签(tag):用来对字段举行描述的元数据,它尽管不属于数据成员,但却是类型信息的组成部分;在运行期,可用反射来赢得字段的标签消息,它常被用作格式检验(如JSON)、数据库关系映射等;标准库reflect.StructTag提供了分/解析标签的效劳;

type user struct{
    name string `昵称`
    sex byte `性别`
}
func main(){
    u:=user{"TOM",1}
    v:=reflect.ValueOf(u)
    t:=v.Type()

    for i,n:=0,t.NumField();i<n;i++{
        fmt.Printf("%s: %v\n", t.Field(i).Tag, v.Field(i))
    }
}
  • 任凭结构体有稍许个字段,它的内存总是一遍性分配的,各字段在附近的地点空间按定义顺序排列(包含嵌入字段的具有
    成员)。对于引用类型、字符串、指针,结构内存中只包含其基本(头部)数据。
  • 结构体在分配内存时,会进展内存对齐处理(依照所有字段中最长的底蕴项目宽度为业内),唯一不同是编译器把空结构类型字段作为最后一个字段时的尺寸视为1来做对齐处理(制止越界)。
  • 内存对齐与硬件平台、以及走访功效有关(CPU在走访自然对齐的数目时索要的读周期更少,还可防止拼接数据)

 

相关文章