从如下几个方面介绍GO语言的数据
1.聽字符串 2.聽数组 3.聽切片 4.聽字典 5.聽结构
字符串
Go语言中的字符串是由一组不可变的字节(byte)序列组成从源码文件中看出其本身是一个复合结构
string.go聽 type聽stringStruct聽struct聽{ 聽聽聽聽str聽unsafe.Pointer聽聽聽聽 聽聽聽聽len聽int }
字符串中的每个字节都是以UTF-8编码存储的Unicode字符字符串的头部指针指向字节数组的开始但是没有NULL或'\0'结尾标志。 表示方式很简单用双引号("")或者反引号(``)它们的区别是
双引号之间的转义符会被转义而反引号之间的转义符保持不变
反引号支持跨行编写而双引号则不可以
{ 聽聽聽聽println("hello\tgo")聽聽聽聽//输出hello聽聽聽聽go 聽聽聽聽println(`hello\tgo`)聽聽聽聽//输出hello\tgo } { 聽聽聽聽println(聽"hello聽 聽聽聽聽go"聽)//syntax聽error:聽unexpected聽semicolon聽or聽newline,聽expecting聽comma聽or聽) 聽聽聽聽println(`hello聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 聽聽聽聽聽聽聽聽go`)聽//可以编译通过 } 输出 hello聽 聽聽聽聽go
在前面类型的章节中描述过字符串的默认值是""而不是nil,比如
var聽s聽string println(聽s聽==聽""聽)聽聽//true println(聽s聽==聽nil聽)聽//invalid聽operation:聽s聽==聽nil聽(mismatched聽types聽string聽and聽nil)
Go字符串支持 "+ , += , == , != , < , >" 六种运算符
Go字符串允许用索引号访问字节数组(非字符)但不能获取元素的地址比如
{聽聽聽聽 聽聽聽聽var聽a聽=聽"hello" 聽聽聽聽println(a[0])聽聽聽聽聽聽聽//输出聽104 聽聽聽聽println(&a[1])聽聽聽聽聽聽//cannot聽take聽the聽address聽of聽a[1] }
Go字符串允许用切片的语法返回子串(起始和结束索引号)比如
var聽a聽=聽"0123456"聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 println(a[:3])聽聽聽聽聽聽//0,1,2 println(a[1:3])聽聽聽聽聽//1,2 println(a[3:])聽聽聽聽聽聽//3,4,5,6
日常开发中经常会有遍历字符串的场景比如
{ 聽聽聽聽var聽a聽=聽"Go语言" 聽聽聽聽for聽i:=0;i聽<聽len(a);i++{聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽//以byte方式按字节遍历 聽聽聽聽聽聽聽聽fmt.Printf("%d:聽[%c]\n",聽i,聽a[i]) 聽聽聽聽} 聽聽聽聽for聽i,聽v聽:=聽range聽a{聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽//以rune方式遍历聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 聽聽聽聽聽聽聽聽fmt.Printf("%d:聽[%c]\n",聽i,聽v) 聽聽聽聽} } 输出聽聽聽聽 0:聽[G] 1:聽[o] 2:聽[è] 3:聽[ˉ] 4:聽[-] 5:聽[è] 6:聽[¨] 7:聽[] 0:聽[G] 1:聽[o] 2:聽[语] 5:聽[言]
在Go语言中字符串的底层使用的是不可以改变的byte数组存的所以在用byte轮询方式时每次得到的只有一个byte而中文字符则是占3个byte的。rune采用计算字符串长度的方式与byte方式不同比如
println(utf8.RuneCountInString(a))聽//聽结果为聽聽4 println(len(a))聽//聽结果为聽8
所以如果想要获得期待的那种结果的话需要先将字符串a转换为rune切片再使用内置的len函数比如:
{ 聽聽聽聽r聽:=聽[]rune(a) 聽聽聽聽for聽i:=聽0;i聽<聽len(r);i++{ 聽聽聽聽聽聽聽聽fmt.Printf("%d:聽[%c]\n",聽i,聽r[i]) 聽聽聽聽} }
所以在遍历或处理的字符串的情况下如果其中存在中文尽量使用rune方式处理。
转换
前面讲过不能修改原字符串如果修改的话需要将字符串转换成[]byte或[]rune , 然后在转换回来比如
{ 聽聽聽聽var聽a聽=聽"hello聽go"聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 聽聽聽聽a[1]聽=聽'd'聽聽聽聽聽聽聽聽聽聽聽聽聽聽//cannot聽assign聽to聽a[1] } { 聽聽聽聽var聽a聽=聽"hello聽go" 聽聽聽聽bs聽:=聽[]byte(a) 聽聽聽聽...聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 聽聽聽聽s2聽:=聽string(bs) 聽聽聽聽rs聽:=聽[]rune(a) 聽聽聽聽... 聽聽聽聽s3聽:=聽string(rs) }
Go语言支持用"+"运算符进行字符串拼接但是每次拼接都需要重新分配内存如果频繁构造一个很长的字符串则性能影响就会很大比如
func聽test1()string{ 聽聽聽聽var聽s聽string 聽聽聽聽for聽i:=聽0;i聽<聽1000聽;i++{ 聽聽聽聽聽聽聽聽s聽+=聽"a"聽 聽聽聽聽}聽聽聽 聽聽聽聽return聽s } func聽Benchmark_test1(b聽*testing.B){ 聽聽聽聽for聽i:=聽0;i聽<聽b.N;聽i++{ 聽聽聽聽聽聽聽聽test1()聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 聽聽聽聽}聽聽聽 } 输出 #聽go聽test聽str1_b_test.go聽聽-bench="test1"聽-benchmem Benchmark_test1-2聽聽聽 聽聽聽聽5000 聽聽聽聽227539聽ns/op 聽聽530338聽B/op 聽聽聽聽聽999聽allocs/op
常用的改进方法是预分配足够的内存空间然后使用strings.Join函数该函数会统计出所有参数的长度并一次性完成内存分配操作改进一下上面的代码
func聽test()string{ 聽聽聽聽s聽:=聽make([]string,1000) 聽聽聽聽for聽i:=聽0;i聽<聽1000聽;i++{ 聽聽聽聽聽聽聽聽s[i]聽=聽"a"聽 聽聽聽聽}聽聽聽 聽聽聽聽return聽strings.Join(s,"") } func聽Benchmark_test(b聽*testing.B){ 聽聽聽聽for聽i:=聽0;i聽<聽b.N;聽i++{ 聽聽聽聽聽聽聽聽test() 聽聽聽聽}聽聽聽 } 输出 #聽go聽test聽-v聽b_test.go聽聽-bench="test1"聽-benchmem Benchmark_test1-2聽聽聽 聽聽200000 聽聽聽聽聽10765聽ns/op 聽聽聽聽2048聽B/op 聽聽聽聽聽聽聽2聽allocs/op
在日常开发中可以使用fmt.Sprintf函数来格式化和拼接较少的字符串操作比如
{ 聽聽聽聽a聽:=聽10010 聽聽聽聽as聽:=聽fmt.Sprintf("%d",a) 聽聽聽聽fmt.Printf("%T聽,聽%v\n",as,as) }
数组
数组是内置(build-in)类型是一组存放相同类型数据的集合数组的数据类型是由存储的元素类型和数组的长度共同决定的,即使元素类型相同但是长度不同数组也不属于同一类型。数组初始化之后长度是固定无法修改的数组也支持逻辑判断运算符 "==","="定义方式如下
{ 聽聽聽聽var聽a聽[10]int 聽聽聽聽var聽b聽[20]int 聽聽聽聽println(a聽==聽b)聽聽//invalid聽operation:聽a聽==聽b聽(mismatched聽types聽[10]int聽and聽[20]int) }
数组的初始化相对灵活下标索引值从0开始支持按索引位置初始化对于未初始化的数组编译器将给以默认值。
{ 聽聽聽聽var聽a[4]聽int聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽//元素初始化为0 聽聽聽聽b聽:=聽[4]聽int{0,1}聽聽聽聽聽聽聽聽聽聽聽//未初始化的元素将被初始化为0 聽聽聽聽c聽:=聽[4]聽int{0,聽2:聽3}聽聽聽聽聽聽聽//可指定索引位置初始化 聽聽聽聽d聽:=聽[...]int{0,1,2}聽聽聽聽聽聽聽聽//编译器根据初始化值数量来确定数组的长度 聽聽聽聽e聽:=聽[...]int{1,聽3:3}聽聽聽聽聽聽聽//支持索引位置初始化但数组长度与其无关 聽聽聽聽 聽聽聽聽type聽user聽struct{ 聽聽聽聽聽聽聽聽name聽string 聽聽聽聽聽聽聽聽age聽int聽 聽聽聽聽}聽聽聽 聽聽聽聽d聽:=聽[...]聽user{聽聽聽聽聽聽聽聽聽聽聽聽//复合数据类型数组可省略元素初始化类型标签 聽聽聽聽聽聽聽聽{"a",1}, 聽聽聽聽聽聽聽聽{"b",2}, 聽聽聽聽} }
定义多维数组时只有数组的第一维度允许使用 "..."
{ 聽聽聽聽x聽:=聽[2]int{2,2} 聽聽聽聽a聽:=聽[2][2]int{{1,2},{2,2}} 聽聽聽聽b聽:=聽[...][2]int{{2,3},{2,2},{3,3}} 聽聽聽聽c聽:=聽[...][2][2]int{{聽{2,3},{2,2}聽},{{3,3},{4,4}}聽} }
计算数组长度时无论使用内置的len还是cap返回的都是第一维度的长度比如
{ 聽聽聽聽fmt.Println(x,聽len(x),聽cap(x))聽聽聽聽 聽聽聽聽fmt.Println(a,聽len(a),聽cap(x)) 聽聽聽聽fmt.Println(b,聽len(b),聽cap(x)) 聽聽聽聽fmt.Println(c,聽len(c),聽cap(x)) } 输出 [2聽2]聽2聽2 [[1聽2]聽[2聽2]]聽2聽2 [[2聽3]聽[2聽2]聽[3聽3]]聽3聽2 [[[2聽3]聽[2聽2]]聽[[3聽3]聽[4聽4]]]聽2聽2
数组指针&指针数组
数组除了可以存放具体类型的数据也可以存放指针比如
{聽 聽聽聽聽x,聽y聽:=聽10,聽20 聽聽聽聽a聽:=聽[...]*int{&x,聽&y}聽聽聽聽聽聽//指针数组聽 聽聽聽聽p聽:=聽&a聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽//数组的指针 }
数组复制
Go语言数组是值(非引用)类型所以在赋值和参数传递过程中都会复制整个数组数据比如:
func聽test(x聽[2]int){ 聽聽聽聽fmt.Printf("x:=聽%p,%v\n",聽&x,聽x) } func聽main(){聽 聽聽聽聽a聽:=聽[2]聽int{1,聽2} 聽聽聽聽test(a)聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽//传参过程中完全复制 聽聽聽聽var聽b聽[2]int 聽聽聽聽b聽=聽a聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽//赋值过程中完全复制 聽聽聽聽fmt.Printf("a:=聽%p,%v\n",聽&a,聽a) 聽聽聽聽fmt.Printf("b:=聽%p,%v\n",聽&b,聽b)聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 } 输出 x:=聽0xc42000a330,[1聽2] a:=聽0xc42000a320,[1聽2] b:=聽0xc42000a370,[1聽2]
借鉴: 雨痕<GO学习笔记>
讨论学习: 675020908
本文出自 “博学于文,约之于礼” 博客,转载请与作者联系!
有疑问加站长微信联系(非本文作者)