一篇带你看懂Go语言中的字符串拼接(小白入门)

Written by

in

文章目录
  • 你想要构造一个输出,比如: "blue is sky the" 最直觉的写法是: ans := “” ans += “blue” ans += ” ” ans += “is” 看起来很合理对吧? 但 Go 的 string 有个关键特性: Go 的 string 不可变(immutable) 你一旦创建了一个 string,它里面的内容不能“原地修改”。 所以 ans += "blue" 实际发生的是: 新开一块内存,长度 = oldLen + newLen 把旧的 ans 复制进去 再把 "blue" 复制进去 把 ans 指向这块新内存 也就是说:每一次 += 都可能要“重新开数组 + 复制一遍旧内容”
  • 举个特别直观的例子: 你要拼 3 次: 第 1 次:复制 0 个旧字符 第 2 次:复制 4 个旧字符("blue") 第 3 次:复制 7 个旧字符("blue is") 你会发现:旧内容一直在被反复复制。 拼得越多,复制越多,速度就越慢。
  • 既然 string 不可变,那我们先用一个 可变的容器 装字符,最后一次性变成 string。 Go 最常用的可变容器就是: []byte(字节数组 / 可变) 你可以对它 append,它会自动增长: ans := make([]byte, 0) ans = append(ans, ‘b’, ‘l’, ‘u’, ‘e’) 最后: return string(ans) 这样就避免了每次 += 的“重新分配 + 复制旧内容”。
  • []byte / []int 这些 slice,在 Go 里有两个重要概念: len:现在已经用了多少 cap:底层总共预留了多少空间(还能装多少) 比如: ans := make([]byte, 0, 10) 意思是: len = 0(现在里面没东西) cap = 10(底层数组先预留了 10 个位置) 为什么要 cap? 因为如果你不预留,append 可能会这样: 空间不够 → 申请更大数组 把旧数组内容复制过去 才能继续 append 所以: cap 就是为了减少“扩容 + 拷贝”的次数。
  • 当你 append 让 len > cap: Go 会创建一个更大的新数组 把旧内容复制到新数组 slice 指向新数组 你看:又出现“复制旧内容”了对吧? 所以我们才要 尽量提前预留 cap。
  • 你用 []byte 拼字符串没问题,但写起来像在操作数组。 strings.Builder 是 Go 官方提供的: 专门用来高效拼接字符串的工具 你可以把它理解成: “官方封装好的[]byte + append” 它的用法是: import “strings” var b strings.Builder b.WriteString(“blue”) b.WriteByte(‘ ‘) b.WriteString(“sky”) result := b.String() 你可以把它理解成: “内部帮你维护了一个 []byte 的拼接器” 你 WriteString/WriteByte,它内部就在 append 到那个 buffer 里。 最后 String() 一次性输出。
  • 现在关键来了: Builder 内部其实也需要容量,否则也会扩容 + 拷贝。 所以它也需要“预留空间”。 这就是: b.Grow(n) 意思是: 提前保证:接下来还能再写 n 个字节,不用扩容 对应到 slice 的感觉就是: slice:make([]byte, 0, n) 预留 cap builder:b.Grow(n) 预留内部 buffer 的 cap 所以它们关系是: Grow 本质上是在给 Builder 内部的“隐藏 slice”扩 cap。
  • 你现在阶段记住这个就够了: 简单场景(拼得不多) 用 + 也行(比如 2~3 次拼接) 循环里大量拼接(比如这题、构造大字符串) 优先用: strings.Builder(推荐,易读)或 []byte + append(你已经在用) 追求性能时加一条:预留容量 ans := make([]byte, 0, len(s)) b.Grow(len(s)) 到此这篇关于一篇带你看懂Go语言中的字符串拼接(小白入门)的文章就介绍到这了,更多相关Go字符串拼接内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客! 您可能感兴趣的文章: GoLang五种字符串拼接方式小结 Go语言中字符串四种拼接方式的性能对比 Golang中拼接字符串的6种方式性能对比 golang字符串拼接实现方式和区别对比 golang拼接字符串的5种实现方式 go字符串拼接方式及性能比拼小结 Go语言字符串拼接方式与性能比较分析 Go语言中字符串拼接的方法总结
  • 目录
    • 1. 为什么“拼字符串”会出问题?
    • 2. 这就是为什么+会慢:重复拷贝
    • 3. 那怎么快?核心思路:用“可变容器”先装起来
    • 4. 这就引出了:len和cap是啥?
    • 5. 超过 cap 会发生什么?
    • 6. 这时候strings.Builder登场:更“像拼字符串”的工具
    • 7. 那Grow又是什么?它和cap的关系是什么?
    • 9. 小白怎么选

    在 Go 里:

    字符串 string 是不可变的(你不能在原来的 string 上直接改)

    所以 + / += 反复拼接,经常会很慢(反复分配 + 复制)

    为了快,Go 常用两种方式:

    • []byte + append
    • strings.Builder 拼(写法更像“拼字符串”,性能也好)

    cap / Grow 都是在做同一件事:提前预留容量,减少扩容拷贝

    你想要构造一个输出,比如:

    "blue is sky the"

    最直觉的写法是:

    ans := ""
    ans += "blue"
    ans += " "
    ans += "is"
    

    看起来很合理对吧?

    但 Go 的 string 有个关键特性:

    Go 的 string 不可变(immutable)

    你一旦创建了一个 string,它里面的内容不能“原地修改”。

    所以 ans += "blue" 实际发生的是:

    • 新开一块内存,长度 = oldLen + newLen
    • 把旧的 ans 复制进去
    • 再把 "blue" 复制进去
    • ans 指向这块新内存

    也就是说:每一次 += 都可能要“重新开数组 + 复制一遍旧内容”

    举个特别直观的例子:

    你要拼 3 次:

    • 第 1 次:复制 0 个旧字符
    • 第 2 次:复制 4 个旧字符("blue")
    • 第 3 次:复制 7 个旧字符("blue is")

    你会发现:旧内容一直在被反复复制。

    拼得越多,复制越多,速度就越慢。

    既然 string 不可变,那我们先用一个 可变的容器 装字符,最后一次性变成 string。

    Go 最常用的可变容器就是:

    []byte(字节数组 / 可变)

    你可以对它 append,它会自动增长:

    ans := make([]byte, 0)
    ans = append(ans, 'b', 'l', 'u', 'e')
    

    最后:

    return string(ans)
    

    这样就避免了每次 += 的“重新分配 + 复制旧内容”。

    []byte / []int 这些 slice,在 Go 里有两个重要概念:

    • len:现在已经用了多少
    • cap:底层总共预留了多少空间(还能装多少)

    比如:

    ans := make([]byte, 0, 10)
    

    意思是:

    • len = 0(现在里面没东西)
    • cap = 10(底层数组先预留了 10 个位置)

    为什么要 cap?

    因为如果你不预留,append 可能会这样:

    • 空间不够 → 申请更大数组
    • 把旧数组内容复制过去
    • 才能继续 append

    所以:

    cap 就是为了减少“扩容 + 拷贝”的次数。

    当你 append 让 len > cap

    • Go 会创建一个更大的新数组
    • 把旧内容复制到新数组
    • slice 指向新数组

    你看:又出现“复制旧内容”了对吧?

    所以我们才要 尽量提前预留 cap

    你用 []byte 拼字符串没问题,但写起来像在操作数组。 strings.Builder 是 Go 官方提供的:

    专门用来高效拼接字符串的工具

    你可以把它理解成:

    “官方封装好的[]byte + append”

    它的用法是:

    import "strings"
    
    var b strings.Builder
    b.WriteString("blue")
    b.WriteByte(' ')
    b.WriteString("sky")
    result := b.String()
    

    你可以把它理解成:

    “内部帮你维护了一个 []byte 的拼接器”

    WriteString/WriteByte,它内部就在 append 到那个 buffer 里。

    最后 String() 一次性输出。

    现在关键来了:

    Builder 内部其实也需要容量,否则也会扩容 + 拷贝。

    所以它也需要“预留空间”。

    这就是:

    b.Grow(n)

    意思是:

    提前保证:接下来还能再写 n 个字节,不用扩容

    对应到 slice 的感觉就是:

    • slice:make([]byte, 0, n) 预留 cap
    • builder:b.Grow(n) 预留内部 buffer 的 cap

    所以它们关系是:

    Grow 本质上是在给 Builder 内部的“隐藏 slice”扩 cap

    你现在阶段记住这个就够了:

    简单场景(拼得不多)

    + 也行(比如 2~3 次拼接)

    循环里大量拼接(比如这题、构造大字符串)

    优先用:

    • strings.Builder(推荐,易读)
    • []byte + append(你已经在用)

    追求性能时加一条:预留容量

    • ans := make([]byte, 0, len(s))
    • b.Grow(len(s))

    到此这篇关于一篇带你看懂Go语言中的字符串拼接(小白入门)的文章就介绍到这了,更多相关Go字符串拼接内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!

    您可能感兴趣的文章:

    • GoLang五种字符串拼接方式小结
    • Go语言中字符串四种拼接方式的性能对比
    • Golang中拼接字符串的6种方式性能对比
    • golang字符串拼接实现方式和区别对比
    • golang拼接字符串的5种实现方式
    • go字符串拼接方式及性能比拼小结
    • Go语言字符串拼接方式与性能比较分析
    • Go语言中字符串拼接的方法总结

    站内搜索