Golang 字符串拼接

字符串拼接应该在编程过程中比较常用的操作了,在Go语言中对字符串的拼接有多种处理方式,以下通过实例来一一讲解

+号拼接

这种应该是最直接最简单的方式了。

func StringPlus() string {
	var s string
	s = "社会主义核心价值观的基本内容:"
	s += "富强、民主、文明、和谐,是我国社会主义现代化国家的建设目标;"
	s += "自由、平等、公正、法治,是对美好社会的生动表述;"
	s += "爱国、敬业、诚信、友善”,是公民基本道德规范。"
	return s
}
复制代码

运行go test -bench=. -benchmem 查看性能输出如下: BenchmarkStringPlus-8 5000000 251 ns/op 640 B/op 3 allocs/op

fmt拼接

func StringFmt() string  {
	return fmt.Sprint("社会主义核心价值观的基本内容:",
		"富强、民主、文明、和谐,是我国社会主义现代化国家的建设目标;,",
		"自由、平等、公正、法治,是对美好社会的生动表述;",
		"爱国、敬业、诚信、友善”,是公民基本道德规范。")
}
复制代码

BenchmarkStringPlus-8 10000000 234 ns/op 288 B/op 1 allocs/op

Join拼接

这个是利用strings.Join函数进行拼接,接受一个字符串数组,转换为一个拼接好的字符串。

func StringJoin() string {
	s := []string{"社会主义核心价值观的基本内容:", "富强、民主、文明、和谐,是我国社会主义现代化国家的建设目标;",
		"自由、平等、公正、法治,是对美好社会的生动表述;",
		"爱国、敬业、诚信、友善”,是公民基本道德规范。"}
	return strings.Join(s, ",")
}
复制代码

BenchmarkStringPlus-8 10000000 189 ns/op 576 B/op 2 allocs/op

buffer

func StringBuffer() string {
	var b bytes.Buffer
	b.WriteString("社会主义核心价值观的基本内容:")
	b.WriteString("富强、民主、文明、和谐,是我国社会主义现代化国家的建设目标;")
	b.WriteString("自由、平等、公正、法治,是对美好社会的生动表述;")
	b.WriteString("爱国、敬业、诚信、友善”,是公民基本道德规范。")

	return b.String()
}
复制代码

BenchmarkStringPlus-8 3000000 505 ns/op 1136 B/op 4 allocs/op

builder

func StringBuilder() string {
	var  s strings.Builder
	s.WriteString("社会主义核心价值观的基本内容:")
	s.WriteString("富强、民主、文明、和谐,是我国社会主义现代化国家的建设目标;")
	s.WriteString("自由、平等、公正、法治,是对美好社会的生动表述;")
	s.WriteString("爱国、敬业、诚信、友善”,是公民基本道德规范。")

	return s.String()
}
复制代码

BenchmarkStringBuffer10-8 10000000 200 ns/op 480 B/op 3 allocs/op

以上是进行了五次字符串的拼接,可以看到buffer的性能较差一些,其他大致三种方式区别不大

那么100个字符串1000个字符串拼接又如何呢

package main

import (
	"bytes"
	"fmt"
	"strings"
)

func StringPlus(p []string) string {
	var s string
	l := len(p)
	for i := 0; i < l; i++ {
		s += p[i]
	}
	return s
}

func StringFmt(p []interface{}) string {
	return fmt.Sprint(p...)
}

func StringJoin(p []string) string {
	return strings.Join(p, "")
}

func StringBuffer(p []string) string {
	var b bytes.Buffer
	l := len(p)
	for i := 0; i < l; i++ {
		b.WriteString(p[i])
	}
	return b.String()
}

func StringBuilder(p []string) string {
	var b strings.Builder
	l := len(p)
	for i := 0; i < l; i++ {
		b.WriteString(p[i])
	}
	return b.String()
}

复制代码

进行如下压测

package main

import "testing"

const WebSite  = "https://www.china.com/"

const StringLen = 1000

func initStrings(N int) []string{
	s:=make([]string,N)
	for i:=0;i<N;i++{
		s[i]=WebSite
	}
	return s
}

func initStringi(N int) []interface{}{
	s:=make([]interface{},N)
	for i:=0;i<N;i++{
		s[i]=WebSite
	}
	return s
}

func BenchmarkStringPlus10(b *testing.B) {
	p:= initStrings(StringLen)
	b.ResetTimer()
	for i:=0;i<b.N;i++{
		StringPlus(p)
	}
}

func BenchmarkStringFmt10(b *testing.B) {
	p:= initStringi(StringLen)
	b.ResetTimer()
	for i:=0;i<b.N;i++{
		StringFmt(p)
	}
}

func BenchmarkStringJoin10(b *testing.B) {
	p:= initStrings(StringLen)
	b.ResetTimer()
	for i:=0;i<b.N;i++{
		StringJoin(p)
	}
}

func BenchmarkStringBuffer10(b *testing.B) {
	p:= initStrings(StringLen)
	b.ResetTimer()
	for i:=0;i<b.N;i++{
		StringBuffer(p)
	}
}

func BenchmarkStringBuilder10(b *testing.B) {
	p:= initStrings(StringLen)
	b.ResetTimer()
	for i:=0;i<b.N;i++{
		StringBuilder(p)
	}
}
复制代码

压测结果如下:

BenchmarkStringPlus10-8             1000           1905639 ns/op        11573410 B/op        999 allocs/op
BenchmarkStringFmt10-8             50000             32464 ns/op           24586 B/op          1 allocs/op
BenchmarkStringJoin10-8           100000             17600 ns/op           49152 B/op          2 allocs/op
BenchmarkStringBuffer10-8          50000             27480 ns/op          122544 B/op         11 allocs/op
BenchmarkStringBuilder10-8        100000             20535 ns/op           96224 B/op         16 allocs/op
复制代码

可以看到Join 和 builder表现最好。但是一般是有数组切片进行字符串拼接我们采用join, 如果没有的话还是builder更合适。

builder 优化

查看WriteString的源码我们可以发现,这里有对b.buf进行append操作,那对于长的字符串就会触发扩容操作影响性能

func (b *Builder) WriteString(s string) (int, error) {
	b.copyCheck()
	b.buf = append(b.buf, s...)
	return len(s), nil
}
复制代码

由于扩容导致的问题,那我们是否可以事先分配好所需的容量呢,查看buIlder源码发现提供了一个Grow方法,正是来进行容量分配的。

func (b *Builder) grow(n int) {
	buf := make([]byte, len(b.buf), 2*cap(b.buf)+n)
	copy(buf, b.buf)
	b.buf = buf
}
复制代码

那我们你可以优化StringBuilder如下:

func StringBuilder(p []string,cap int) string {
	var b strings.Builder
	l:=len(p)
	b.Grow(cap)
	for i:=0;i<l;i++{
		b.WriteString(p[i])
	}
	return b.String()
}
复制代码

本文亦在微信公众号【小道资讯】发布,欢迎扫码关注!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值