golang 数据一 (字符串、数组和数组指针)
从如下几个方面介绍GO语言的数据
1.字符串2.数组3.切片4.字典5.结构字符串
Go语言中的字符串是由一组不可变的字节(byte)序列组成从源码文件中看出其本身是一个复合结构
string.gotypestringStructstruct{strunsafe.Pointerlenint}
字符串中的每个字节都是以UTF-8编码存储的Unicode字符字符串的头部指针指向字节数组的开始但是没有NULL或'\0'结尾标志。 表示方式很简单用双引号("")或者反引号(``)它们的区别是
双引号之间的转义符会被转义而反引号之间的转义符保持不变
反引号支持跨行编写而双引号则不可以
{println("hello\tgo")//输出hellogoprintln(`hello\tgo`)//输出hello\tgo}{println("hellogo")//syntaxerror:unexpectedsemicolonornewline,expectingcommaor)println(`hellogo`)//可以编译通过}输出hellogo
在前面类型的章节中描述过字符串的默认值是""而不是nil,比如
varsstringprintln(s=="")//trueprintln(s==nil)//invalidoperation:s==nil(mismatchedtypesstringandnil)
Go字符串支持 "+ , += , == , != , < , >" 六种运算符
Go字符串允许用索引号访问字节数组(非字符)但不能获取元素的地址比如
{vara="hello"println(a[0])//输出104println(&a[1])//cannottaketheaddressofa[1]}
Go字符串允许用切片的语法返回子串(起始和结束索引号)比如
vara="0123456"println(a[:3])//0,1,2println(a[1:3])//1,2println(a[3:])//3,4,5,6
日常开发中经常会有遍历字符串的场景比如
{vara="Go语言"fori:=0;i<len(a);i++{//以byte方式按字节遍历fmt.Printf("%d:[%c]\n",i,a[i])}fori,v:=rangea{//以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))//结果为4println(len(a))//结果为8
所以如果想要获得期待的那种结果的话需要先将字符串a转换为rune切片再使用内置的len函数比如:
{r:=[]rune(a)fori:=0;i<len(r);i++{fmt.Printf("%d:[%c]\n",i,r[i])}}
所以在遍历或处理的字符串的情况下如果其中存在中文尽量使用rune方式处理。
转换
前面讲过不能修改原字符串如果修改的话需要将字符串转换成[]byte或[]rune , 然后在转换回来比如
{vara="hellogo"a[1]='d'//cannotassigntoa[1]}{vara="hellogo"bs:=[]byte(a)...s2:=string(bs)rs:=[]rune(a)...s3:=string(rs)}
Go语言支持用"+"运算符进行字符串拼接但是每次拼接都需要重新分配内存如果频繁构造一个很长的字符串则性能影响就会很大比如
functest1()string{varsstringfori:=0;i<1000;i++{s+="a"}returns}funcBenchmark_test1(b*testing.B){fori:=0;i<b.N;i++{test1()}}输出#goteststr1_b_test.go-bench="test1"-benchmemBenchmark_test1-25000227539ns/op530338B/op999allocs/op
常用的改进方法是预分配足够的内存空间然后使用strings.Join函数该函数会统计出所有参数的长度并一次性完成内存分配操作改进一下上面的代码
functest()string{s:=make([]string,1000)fori:=0;i<1000;i++{s[i]="a"}returnstrings.Join(s,"")}funcBenchmark_test(b*testing.B){fori:=0;i<b.N;i++{test()}}输出#gotest-vb_test.go-bench="test1"-benchmemBenchmark_test1-220000010765ns/op2048B/op2allocs/op
在日常开发中可以使用fmt.Sprintf函数来格式化和拼接较少的字符串操作比如
{a:=10010as:=fmt.Sprintf("%d",a)fmt.Printf("%T,%v\n",as,as)}
数组
数组是内置(build-in)类型是一组存放相同类型数据的集合数组的数据类型是由存储的元素类型和数组的长度共同决定的,即使元素类型相同但是长度不同数组也不属于同一类型。数组初始化之后长度是固定无法修改的数组也支持逻辑判断运算符 "==","="定义方式如下
{vara[10]intvarb[20]intprintln(a==b)//invalidoperation:a==b(mismatchedtypes[10]intand[20]int)}
数组的初始化相对灵活下标索引值从0开始支持按索引位置初始化对于未初始化的数组编译器将给以默认值。
{vara[4]int//元素初始化为0b:=[4]int{0,1}//未初始化的元素将被初始化为0c:=[4]int{0,2:3}//可指定索引位置初始化d:=[...]int{0,1,2}//编译器根据初始化值数量来确定数组的长度e:=[...]int{1,3:3}//支持索引位置初始化但数组长度与其无关typeuserstruct{namestringageint}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))}输出[22]22[[12][22]]22[[23][22][33]]32[[[23][22]][[33][44]]]22
数组指针&指针数组
数组除了可以存放具体类型的数据也可以存放指针比如
{x,y:=10,20a:=[...]*int{&x,&y}//指针数组p:=&a//数组的指针}
数组复制
Go语言数组是值(非引用)类型所以在赋值和参数传递过程中都会复制整个数组数据比如:
functest(x[2]int){fmt.Printf("x:=%p,%v\n",&x,x)}funcmain(){a:=[2]int{1,2}test(a)//传参过程中完全复制varb[2]intb=a//赋值过程中完全复制fmt.Printf("a:=%p,%v\n",&a,a)fmt.Printf("b:=%p,%v\n",&b,b)}输出x:=0xc42000a330,[12]a:=0xc42000a320,[12]b:=0xc42000a370,[12]
借鉴: 雨痕<GO学习笔记>
讨论学习: 675020908
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。