浅析Golang中字符串拼接问题

 更新时间:2023年04月11日 14:15:25   作者:落风雪  
Go的字符串是一个不可改变的数据结构,这和其他语言如JAVA,C++等的设定很类似.总体来说,有如下五种拼接方式,下面我们将论述各种方式的性能问题,以及如何选择

1.概述

Go的字符串是一个不可改变的数据结构,这和其他语言如JAVA,C++等的设定很类似.总体来说,有如下五种拼接方式,下面我们将论述各种方式的性能问题,以及如何选择.

(golang字符串,内存模型)

type StringHeader struct {
	Data uintptr
	Len  int
}

注意:字符串具有不可改变的特性,即便通过指针等变相操作

	var a string = "old"
	bptr := (*reflect.StringHeader)(unsafe.Pointer(&a))
	dataPtr := (*byte)(unsafe.Pointer(bptr.Data))
	var b = [3]byte{'n', 'e', 'w'}
	*dataPtr = b[0] //报错
	fmt.Println(bptr)

2.Golang中字符串拼接的方式

方式一、直接+

当使用连接符 + 拼接两个字符串时,会生成一个新的字符串并开辟新的内存空间,空间大小等于两个字符串之和。在训中中时,不断拼接新的字符串,这样就会不断申请内存空间, 性能就会越来越差。 所以,在字符串密集拼接场景中,使用 + 会严重降低性能。包括热路径的代码.

方式二、strings.Builder

func Benchmark_StringsBuilder(b *testing.B) {
	var sb strings.Builder
	for i := 0; i < b.N; i++ {
		sb.WriteString("hello world")
	}
	_ = sb.String()
}

方式三、bytes.Buffer

func Benchmark_BytesBuffer(b *testing.B) {
	var buf bytes.Buffer
	for i := 0; i < b.N; i++ {
		buf.WriteString("hello world")
	}
	_ = buf.String()
}

方式四、fmt.Fprint(&buf,&str)

方式五、strings.Join

性能不是最优,但在切片的情况下,可以用来拼接

3.总结

Benchmark_StringAdd Benchmark_StringAdd-8 117806 127059 ns/op Benchmark_BytesBuffer Benchmark_BytesBuffer-8 38938282 25.88 ns/op Benchmark_StringsBuilder Benchmark_StringsBuilder-8 57249450 18.53 ns/op

3.1 性能方面,strings.Builder 比 bytes.Buffer 快差不多 20%,

原因:strings.Builder 和 bytes.Buffer 底层都是一个 []byte,但是 bytes.Buffer 转换字符串时会重新申请内存空间用来存放, 而 strings.Builder 直接将底层的 []byte 利用指针的方式强转为字符串.

//strings.Builder的String()
func (b *Builder) String() string {
    return *(*string)(unsafe.Pointer(&b.buf))
}

//bytes.Builder的String()
func (b *Buffer) String() string {
    if b == nil {
        // Special case, useful in debugging.
        return "<nil>"
    }
    return string(b.buf[b.off:])
}

3.2 strings.Builder通常性能最优,但底层依赖于[]byte,所以如果平凡扩容就不妙了,因此我们需要借助它的Grow方法,以已分配最终[]byte的容量,避免因为扩容带来的性能损失

func Benchmark_StringConcat(b *testing.B) {
    str := "hello world"
    var sb strings.Builder
    sb.Grow(b.N * len(str))
    for i := 0; i < b.N; i++ {
        sb.WriteString(str)
    }
    _ = sb.String()
}

3.3 strings.Builder没有拷贝构造(借用C++说法),因为

type Builder struct {  
addr *Builder // of receiver, to detect copies by value  
buf []byte  //如果拷贝,这个buf共享,最后导致数据混乱
}

到此这篇关于浅析Golang中字符串拼接问题的文章就介绍到这了,更多相关Golang字符串拼接内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Go语言kube-scheduler深度剖析与开发之pod调度

    Go语言kube-scheduler深度剖析与开发之pod调度

    这篇文章主要为大家介绍了Go语言kube-scheduler深度剖析与开发,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • 详解Go语言中单链表的使用

    详解Go语言中单链表的使用

    链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。本文将通过实例为大家详解Go语言中单链表的常见用法,感兴趣的可以了解一下
    2022-08-08
  • Go语言学习之context包的用法详解

    Go语言学习之context包的用法详解

    日常Go开发中,Context包是用的最多的一个了,几乎所有函数的第一个参数都是ctx,那么我们为什么要传递Context呢,Context又有哪些用法,底层实现是如何呢?相信你也一定会有探索的欲望,那么就跟着本篇文章,一起来学习吧
    2022-10-10
  • 基于Go+WebSocket实现实时通信功能

    基于Go+WebSocket实现实时通信功能

    在互联网应用程序中,实时通信是一种非常重要的功能,WebSocket 是一种基于 TCP 的协议,它允许客户端和服务器之间进行双向通信,本文将介绍如何使用 Golang 创建单独的 WebSocket 会话,以实现实时通信功能,需要的朋友可以参考下
    2023-10-10
  • Go语言实现Fibonacci数列的方法

    Go语言实现Fibonacci数列的方法

    这篇文章主要介绍了Go语言实现Fibonacci数列的方法,实例分析了使用递归和不使用递归两种技巧,并对算法的效率进行了对比,需要的朋友可以参考下
    2015-02-02
  • go proto编译引用外部包问题解决方案示例

    go proto编译引用外部包问题解决方案示例

    这篇文章主要为大家介绍了go proto编译引用外部包问题解决方案示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-10-10
  • 在Golang中正确的修改HTTPRequest的Host的操作方法

    在Golang中正确的修改HTTPRequest的Host的操作方法

    我们工作中经常需要通过HTTP请求Server的服务,比如脚本批量请求接口跑数据,由于一些网关策略,部分Server会要求请求中Header里面附带Host参数,所以本文给大家介绍了如何在Golang中正确的修改HTTPRequest的Host,需要的朋友可以参考下
    2023-12-12
  • Go单元测试对数据库CRUD进行Mock测试

    Go单元测试对数据库CRUD进行Mock测试

    这篇文章主要为大家介绍了Go单元测试对数据库CRUD进行Mock测试的示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • Go语言题解LeetCode455分发饼干示例详解

    Go语言题解LeetCode455分发饼干示例详解

    这篇文章主要为大家介绍了Go语言题解LeetCode455分发饼干示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • 下载golang.org/x包的操作方法

    下载golang.org/x包的操作方法

    今天小编就为大家分享一篇下载golang.org/x包的操作方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-07-07

最新评论