1. 数据量较小且无并发操作
- 方式:使用
bytes.Buffer
结构体的 Write
系列方法。bytes.Buffer
是一个可变大小的字节缓冲区,其 Write
方法效率较高,适用于一般的字节拼接场景。
- 原理:
bytes.Buffer
内部维护一个字节切片,当写入的数据超过当前切片容量时,会自动扩容,扩容策略采用成倍增长的方式,减少内存分配次数。
- 示例代码:
package main
import (
"bytes"
"fmt"
)
func main() {
var b bytes.Buffer
data1 := []byte("Hello, ")
data2 := []byte("world!")
b.Write(data1)
b.Write(data2)
result := b.Bytes()
fmt.Println(string(result))
}
2. 数据量较大且无并发操作
- 方式:预分配足够的空间给
bytes.Buffer
。通过 bytes.NewBuffer(make([]byte, 0, initialCapacity))
方式创建 bytes.Buffer
,其中 initialCapacity
是预估的数据量大小。这样可以避免在拼接过程中频繁的内存扩容操作,从而提升性能。
- 原理:内存扩容涉及内存的重新分配和数据复制,开销较大。预分配足够空间可减少这种开销。
- 示例代码:
package main
import (
"bytes"
"fmt"
)
func main() {
// 假设预估总数据量为1024字节
b := bytes.NewBuffer(make([]byte, 0, 1024))
data1 := []byte("a string with some data...")
data2 := []byte("another part of data...")
b.Write(data1)
b.Write(data2)
result := b.Bytes()
fmt.Println(string(result))
}
3. 存在并发操作
- 方式:使用
sync.Pool
与 bytes.Buffer
结合。sync.Pool
可以缓存并复用临时对象,减少内存分配压力,特别适合高并发场景。
- 原理:
sync.Pool
维护了一个本地池和全局池,在高并发下,每个 goroutine 优先从本地池中获取对象,如果本地池为空则尝试从全局池获取,使用完对象后再放回池中以便复用。
- 示例代码:
package main
import (
"bytes"
"fmt"
"sync"
)
var bufferPool = sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
}
func concateBytes(dataList [][]byte) []byte {
buffer := bufferPool.Get().(*bytes.Buffer)
defer bufferPool.Put(buffer)
buffer.Reset()
for _, data := range dataList {
buffer.Write(data)
}
return buffer.Bytes()
}
func main() {
var wg sync.WaitGroup
var resultList []byte
dataLists := [][][]byte{
{[]byte("data1"), []byte("data2")},
{[]byte("data3"), []byte("data4")},
}
for _, dataList := range dataLists {
wg.Add(1)
go func(dl [][]byte) {
defer wg.Done()
result := concateBytes(dl)
resultList = append(resultList, result...)
}(dataList)
}
wg.Wait()
fmt.Println(string(resultList))
}