《Go语言学习笔记》读书笔记(7)数据结构

少于 1 分钟阅读

数据结构

字符串

字符串是不可变字节(byte)序列,其本身是一个符合结构.

type stringStruct struct {
  str unsafe.Pointer
  len int
}

头部指针指向字节数组,但没有NULL结尾。默认以UTF-8编码存储Unicode字符,字面量里允许使用十六进制、八进制和UTF编码格式。

内置函数len返回字节数组长度,cap不接受字符串类型参数。

字符串默认值不是nil, 而是"".

使用for遍历字符串是,分byterune两种方式。

func main () {	// byte
	s:="李建聪"
	for i := 0; i < len(s); i++ {
		fmt.Printf("%d: [%c]\n", i, s[i])
	}


	for i, c := range s {	// rune: 返回数组索引号,以及Unicode字符
		fmt.Printf("%d: [%c]\n", i, c)
	}
}
///0: [æ]
///1: [
///2: []
///3: [å]
///4: [»]
///5: [º]
///6: [è]
///7: []
///8: [ª]
///0: [李]
///3: [建]
///6: [聪]

要修改字符串,须将其转换为可变类型([]rune[]byte), 待完成后再转换回来。但不管怎么转换,都须重新分配内存,并复制数据。

func pp(format string, ptr interface{}) {
	p := reflect.ValueOf(ptr).Pointer()
	h := (*uintptr)(unsafe.Pointer(p))
	fmt.Printf(format, *h)
}

func main () {	// byte
	s:="hello, world!"
	pp("s: %x\n", &s)

	bs := []byte(s)
	s2 := string(bs)

	pp("string to []byte, bs:%x\n", &bs)
	pp("[]byte to string, s2:%x\n", &s2)

	rs := []rune(s)
	s3 := string(rs)

	pp("string to []byte, rs:%x\n", &rs)
	pp("[]byte to string, s3:%x\n", &s3)
}
/// 输出:
/// s: 2bcc91
/// string to []byte, bs:c0000a20a0
/// []byte to string, s2:c0000a20b0
/// string to []byte, rs:c0000b80c0
/// []byte to string, s3:c0000a20d0

编译器会为了某些场合进行专门优化,避免额外分配和复制操作:

  • []byte转换为string key, 去map[string]查询的时候。
  • string转换为[]byte, 进行for range迭代时,直接取字节赋值给局部变量。

除了类型转换外,动态构建字符串也容易造成性能问题。 用加法操作符拼接字符串时,每次都须重新分配内存。如此,在构建超大字符串时,性能就显得极差。 改进思路时预分配i足够大的空间。常用方法是用string.Join函数,他会统计所有参数长度,并一次性完成内存分配操作。 另外 utf8.ValidString(s) 返回s是不是一个有效的字符串 utf8.RuneCountInString(s) 替代len返回unicode的字符数量