Go 编程语言的标准库公开了一个名为 的结构strings.Builder,它允许通过重复连接以有效的方式轻松构建字符串,类似于 C# 或 Java 的StringBuilder.
在 Java 中,我会使用StringBuilder的构造函数来“克隆”对象,如下所示:
StringBuilder newBuffer = new StringBuilder(oldBuffer.toString());
Run Code Online (Sandbox Code Playgroud)
在Go中,我只能看到以下两行方式:
newBuffer := strings.Builder{}
newBuffer.WriteString(oldBuffer.String())
Run Code Online (Sandbox Code Playgroud)
没有其他.Clone()初始化方法(我可能还没有找到)。
是否有另一种方法比我提出的方法更简短/简洁?
出于好奇而进入不必要的细节......
考虑完文档后,以下是您的主要问题:
Builder.String方法。Builder一旦操作了值,复制它就不安全了。我们来看看这个版本:
newBuffer := strings.Builder{}
newBuffer.WriteString(oldBuffer.String())
Run Code Online (Sandbox Code Playgroud)
我首先想到为什么这是不可取的,因为Builder内部使用字节切片(可变数据类型),并返回字符串(不可变数据类型)。尽管字符串的底层表示与字节切片相同,但由于这种可变性规则,它需要一个副本才能转换为字符串。这意味着当您将字符串写入新缓冲区时,您已经在第二个副本上,而您的任务直观上只需要一个副本。
然而,实际上看一下源代码,我们会发现这个假设是错误的:
func (b *Builder) String() string {
return *(*string)(unsafe.Pointer(&b.buf))
}
Run Code Online (Sandbox Code Playgroud)
使用该unsafe包,该strings包基本上将缓冲区([]byte)直接“破解”为string. 同样,这些数据类型在内存级别上是相同的:指向字符串或切片开头的指针,以及描述字符串或切片长度的指针偏移量。这些数据类型只是标头,因此此处没有发生缓冲区复制。
这会造成一种不舒服的情况,即您有一个应该是不可变的字符串,但您仍然在某个地方有一个字节切片,可能会改变这些底层字节。包unsafe毕竟被调用了,这是一个很好的例子来说明为什么会这样。
因为它strings.Builder纯粹是一个“构建器”,即它只能创建字符串的新部分,并且永远不会修改已写入的数据,所以我们仍然获得语言“保证”的字符串的不变性。我们打破该规则的唯一方法是访问 的内部buf,Builder但由于该字段未导出,您将再次需要unsafe自己去访问它。
您想出的简单方法,虽然可能比预期的长一行(或两行),但它是确定且正确的方法。即使您推出了 Go 的更强大的功能(如unsafe和 ) ,它也已经和您将获得的一样高效reflect。
我希望这能提供一些信息。以下是对代码的唯一建议更改:
// clone the builder contents. this is fast.
newBuffer := strings.Builder{}
newBuffer.WriteString(oldBuffer.String())
Run Code Online (Sandbox Code Playgroud)