Loading

Go语言精进之路读书笔记第15条——了解string实现原理并高效使用

15.1 Go语言的字符串类型

在Go语言中,无论是字符串常量、字符串变量还是代码中出现的字符串字面量,它们的类型都被统一设置为string

特点

  1. string类型的数据是不可变的
    • 对string进行切片化后,Go编译器会为切片变量重新分配底层存储而不是共用string的底层存储
    • string的底层的数据存储区仅能进行只读操作,一旦试图修改那块区域的数据,便会得到SIGBUS的运行时错误
  2. 零值可用
  3. 获取长度的时间复杂度是O(1)级别
  4. 支持通过+/+=操作符进行字符串连接
  5. 支持各种比较关系操作符:==、!= 、>=、<=、>和<
  6. 对非ASCII字符提供原生支持
  7. 原生支持多行字符串(使用 ``)
  8. 直接将string类型通过函数/方法参数传入也不会有太多的损耗,因为传入的仅仅是一个“描述符”,而不是真正的字符串数据

15.2 字符串的内部表示

//todo

15.3 字符串的高效构造

  • 在能预估出最终字符串长度的情况下,使用预初始化的strings.Builder连接构建字符串效率最高;
  • strings.Join连接构建字符串的平均性能最稳定,如果输入的多个字符串是以[]string承载的,那么strings.Join也是不错的选择;类似implode
  • 使用操作符连接的方式最直观、最自然,在编译器知晓欲连接的字符串个数的情况下,使用此种方式可以得到编译器的优化处理;
  • fmt.Sprintf虽然效率不高,但也不是一无是处,如果是由多种不同类型变量来构建特定格式的字符串,那么这种方式还是最适合的。

15.4 字符串相关的高效转换

下面两种互相转换都要分配新内存

[]byte
b := []byte(s)
ss := string(bb)

[]rune
r := []rune(s)
ss := string(rr)

无需额外分配新内存的转换方式

func convert() {
    s := "中国欢迎您,北京欢迎您"
    sl := []byte(s)
    for _, v := range sl {
        _ = v
    }
}
func convertWithOptimize() {
    s := "中国欢迎您,北京欢迎您"
    for _, v := range []byte(s) {
        _ = v
    }
}

func stringForRangeCovertOptimize() {
    fmt.Println(testing.AllocsPerRun(1, convert)) //1
    fmt.Println(testing.AllocsPerRun(1, convertWithOptimize)) //0
}

关于rune的知识点

// rune is an alias for int32 and is equivalent to int32 in all ways. It is // used, by convention, to distinguish character values from integer values.  
//int32的别名,几乎在所有方面等同于int32 
//它用来区分字符值和整数值  
type rune = int32 

问题:string是怎么转成[]rune的呢?v是什么呢?

  • string通过类型转换,来转成[]rune
  • for range遍历string或[]rune时,v都表示每一个Unicode字符的值(rune类型)
s := "hi,你好吗"
r := []rune(s)
for i, v := range r {
    fmt.Printf("%d %x\n", i, v)
}
for i, v := range s {
    fmt.Printf("%d %x\n", i, v)
}

fmt.Printf("%x", s)
posted @ 2024-02-07 17:38  brynchen  阅读(6)  评论(0编辑  收藏  举报