当前位置: 首页 > news >正文

golang中的字符串拼接

go中常见的字符串拼接方法

假设我们现在要实现这样一个拼接函数: 将字符串重复n次拼接起来,返回一个新字符串。

方法一:使用+运算符

func simpleSplice(s string, n int) string {newStr := ""for i := 0; i < n; i++ {newStr += s}return newStr
}

方法二:使用Sprintf

func sprintfSplice(s string, n int) string {newStr := ""for i := 0; i < n; i++ {newStr = fmt.Sprintf("%s%s", newStr, s)}return newStr
}

方法三:使用[]byte

func bytesSplice(s string, n int) string {newStr := []byte{}for i := 0; i < n; i++ {newStr = append(newStr, []byte(s)...)}return string(newStr)
}

方法四:使用bytes.Buffer

func bufferSplice(s string, n int) string {buffer := bytes.Buffer{}for i := 0; i < n; i++ {buffer.WriteString(s)}return buffer.String()
}

方法五:使用strings.Builder

func builderSplice(s string, n int) string {builder := strings.Builder{}for i := 0; i < n; i++ {builder.WriteString(s)}return builder.String()
}

性能测试

我们对上面五种方法进行benchmark测试。

测试函数类似这样,生成100长度的随机字符串,然后拼接100次。

func BenchmarkBytesSplice3(b *testing.B) {for i := 0; i < b.N; i++ {bytesSplice3(genStr(100), 100)}
}

结果如下:

BenchmarkSimpleSplice
BenchmarkSimpleSplice-12     	    9901	    123436 ns/op
BenchmarkSprintfSplice
BenchmarkSprintfSplice-12    	    8151	    144824 ns/op
BenchmarkBytesSplice
BenchmarkBytesSplice-12      	   62271	     19435 ns/op
BenchmarkBuilderSplice
BenchmarkBuilderSplice-12    	   93918	     11890 ns/op
BenchmarkBufferSplice
BenchmarkBufferSplice-12     	   97413	     11816 ns/op

以上仅测试了一次, 不要求严谨, 只是为了说明问题

可以看见使用+运算符拼接和Sprintf的性能是最差的。

+运算符/Sprintf

+运算符和Sprintf性能差的原因其实差不多: 每次都会创建一个新的字符串,然后将原来的字符串和新的字符串拼接起来,这样就会产生很多的临时字符串,这些临时字符串会占用很多的内存,而且还会增加GC的负担。

bytes/strings.Builder/buffer

这三者都是利用[]byte实现的功能,所以从当前这个测试中来看差别不大(对比上面那两个来说~)。

我们查看buffer的源码发现, 他每次写入之前会计算好所需的空间, 然后将其copy[]byte中。

Builder中虽然使用append追加的数据, 但是使用了unsafe方法直接操作内存指针。

所以操作[]byte来实现拼接是最快, 而bufferBuilder带来的内存优化有多少呢?

内存占用

我们调大需要测试的字符串长度, 这样会更明显: 字符串长度1000, 拼接1000次。

func BenchmarkBytesSplice3(b *testing.B) {for i := 0; i < b.N; i++ {bytesSplice3(genStr(1000), 1000)}
}

运行结果

BenchmarkBytesSplice3-12            2421            521164 ns/op         2033674 B/op       1003 allocs/op
BenchmarkBuilderSplice-12           1135            986065 ns/op         5238292 B/op         26 allocs/op
BenchmarkBufferSplice-12            2733            451784 ns/op         3105806 B/op         14 allocs/op

BytesSplice3为什么会有1003 allocs ? 明明已经预分配内存了

func bytesSplice3(s string, n int) string {// 预分配内存   newStr := make([]byte, 0, len(s)*n)for i := 0; i < n; i++ {// 问题在这里, 每次都会创建一个新的[]byte// 其实这个动作可以省略掉newStr = append(newStr, []byte(s)...)// 更换成这种写法试试// newStr = append(newStr, s...)}return *(*string)(unsafe.Pointer(&newStr))
}

更换后:

BenchmarkBytesSplice3-12            8913            135231 ns/op         1009667 B/op          3 allocs/op
BenchmarkBuilderSplice-12           1530            756342 ns/op         5238292 B/op         26 allocs/op
BenchmarkBufferSplice-12            3198            381564 ns/op         3105808 B/op         14 allocs/op

总结

在可预知拼接结果长度的情况下, 使用make([]byte, 0, len(s)*n)这样的方式来预分配内存是最合适的。 其他情况下, 使用strings.Builder, buffer都是可以的。

相关文章:

  • MT8766安卓核心板/开发板_MTK联发科4G安卓手机主板方案定制开发
  • 2023年全国职业院校技能大赛软件测试赛题—单元测试卷⑧
  • 联手英特尔,释放星飞分布式全闪存储潜能
  • 用python调用Mybatis
  • RISC-V Bytes: Caller and Callee Saved Registers
  • SSH镜像、systemctl镜像、nginx镜像、tomcat镜像
  • C#编程-属性和反射
  • 从CISC到RISC-V:揭开指令集的面纱
  • 使用 PyQt 实现简单数据绑定和组件化
  • 文献阅读:Large Language Models as Optimizers
  • ZZULIOJ 1112: 进制转换(函数专题)
  • 【JaveWeb教程】(26) Mybatis基础操作(新增、修改、查询、删除) 详细代码示例讲解(最全面)
  • 解决方案类常用网址
  • linux如何创建文件教程分享
  • Ubuntu 22.04 Cron使用
  • 【Under-the-hood-ReactJS-Part0】React源码解读
  • 【跃迁之路】【444天】程序员高效学习方法论探索系列(实验阶段201-2018.04.25)...
  • 【跃迁之路】【641天】程序员高效学习方法论探索系列(实验阶段398-2018.11.14)...
  • CAP理论的例子讲解
  • CentOS 7 防火墙操作
  • eclipse的离线汉化
  • Java Agent 学习笔记
  • Linux中的硬链接与软链接
  • mongodb--安装和初步使用教程
  • Perseus-BERT——业内性能极致优化的BERT训练方案
  • Promise面试题,控制异步流程
  • Ruby 2.x 源代码分析:扩展 概述
  • SpingCloudBus整合RabbitMQ
  • 短视频宝贝=慢?阿里巴巴工程师这样秒开短视频
  • 翻译:Hystrix - How To Use
  • 机器学习中为什么要做归一化normalization
  • 如何解决微信端直接跳WAP端
  • 深入 Nginx 之配置篇
  • 突破自己的技术思维
  • 为视图添加丝滑的水波纹
  • 想写好前端,先练好内功
  • 追踪解析 FutureTask 源码
  • 如何正确理解,内页权重高于首页?
  • ​3ds Max插件CG MAGIC图形板块为您提升线条效率!
  • ### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTr
  • #【QT 5 调试软件后,发布相关:软件生成exe文件 + 文件打包】
  • #pragam once 和 #ifndef 预编译头
  • #每日一题合集#牛客JZ23-JZ33
  • (cos^2 X)的定积分,求积分 ∫sin^2(x) dx
  • (第9篇)大数据的的超级应用——数据挖掘-推荐系统
  • (简单) HDU 2612 Find a way,BFS。
  • (七)微服务分布式云架构spring cloud - common-service 项目构建过程
  • (未解决)jmeter报错之“请在微信客户端打开链接”
  • (转)Android学习系列(31)--App自动化之使用Ant编译项目多渠道打包
  • .NET Compact Framework 3.5 支持 WCF 的子集
  • .NET delegate 委托 、 Event 事件,接口回调
  • .NET MVC第五章、模型绑定获取表单数据
  • .netcore 如何获取系统中所有session_如何把百度推广中获取的线索(基木鱼,电话,百度商桥等)同步到企业微信或者企业CRM等企业营销系统中...
  • .so文件(linux系统)
  • /var/log/cvslog 太大