【导读】go的strings Builder和bytes Buffser在转换字符串的实现上有什么不同?本文做了详细解析。

strings.Builderbytes.Buffer底层都是[]byte, 为什么strings.Builder的String()方法比bytes.Buffer的要快?

unsafe.Pointer 的用法

Pointer类型代表了任意一种类型的指针,类型Pointer有四种专属的操作:

  • 任意类型的指针能够被转换成Pointer值
  • 一个Pointer值能够被转换成任意类型的指针值
  • 一个uintptr值能够被转换从Pointer值
  • 一个Pointer值能够被转换成uintptr值

因此Pointer类型允许一个程序绕过类型系统,读,写任意内存。它应该被非常谨慎地使用

Pointer通过特定的模式来使用。如果某些代码不以 Go 文档中指定的模式来使用,Go 将无法保证它在现在或者未来是有效的 下面的每个模式都有非常重要的注意事项。

运行go vet命令可以检测Pointer的用法是否超出 Go 文档中指定的模式, 但是go vet没有检测到异常并不代表所检测的代码一定是有效的。

下面我们将简单介绍类型转换模式,更多的模式请参考 Go Pointer 文档

类型转换模式

可以通过*T1 => Pointer => *T2的方式来完成类型转换。

需要保证 T2的内存 <= T1的内存,而且转换以后它们使用的是同一片内存数据。这个转换允许将一片内存区中的数据从一种类型变成另外一种类型。使用这个转换的一个例子是math.Float64bits

func Float64bits(f float64) uint64 {
  return *(*uint64)(unsafe.Pointer(&f))
}

针对 []byte 和 string 执行类型转换

了解了类型转换模式后,我们来看一段 []byte 转换成 string 的代码:

package main

import (
 "fmt"
 "unsafe"
)

func main() {
 var b = []byte{'H', 'E', 'L', 'L', 'O'}

 s := *(*string)(unsafe.Pointer(&b))

 fmt.Println("b =", b)
 fmt.Println("s =", s)

 b[1] = 'B'
 fmt.Println("s =", s)

 s = "WORLD"
 fmt.Println("b =", b)
 fmt.Println("s =", s)

 //b = [72 69 76 76 79]
 //s = HELLO
 //s = HBLLO
 //b = [72 66 76 76 79]
 //s = WORLD
}

可以看到,将b []byte转换成s string后,他们还是用同一片内存空间, 所以针对b的改变也会影响到s。但是对s重新赋值后,它们所使用的内存空间不同了,所以s改变后,b并不会改变。

比较 strings.Builder 和 bytes.Buffer

strings.Builderbytes.Buffer底层都是使用[]byte实现的, 但是性能测试的结果显示, 执行String()函数的时候,strings.Builder却比bytes.Buffer快很多。

区别就在于 bytes.Buffer 是重新申请了一块空间,存放生成的string变量, 而strings.Builder直接将底层的[]byte转换成了string类型返回了回来。

bytes.Buffer中也说明了,如果想更有效率地(efficiently)构建字符串,请使用strings.Builder类型

bytes.Buffer

// String returns the contents of the unread portion of the buffer
// as a string. If the Buffer is a nil pointer, it returns "<nil>".
//
// To build strings more efficiently, see the strings.Builder type.
func (b *Buffer) String() string {
 if b == nil {
  // Special case, useful in debugging.
  return "<nil>"
 }
 return string(b.buf[b.off:])
}

strings.Builder

// String returns the accumulated string.
func (b *Builder) String() string {
 return *(*string)(unsafe.Pointer(&b.buf))
}

转自:bwangel

bwangel.me/2019/04/28/byte-vs-builder/